/* ptpcam.c
 *
 * Copyright (C) 2001-2005 Mariusz Woloszyn <emsi@ipartners.pl>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Modified by:
 * Jin Li
 * Quang Vinh Luong
 * Zhexin Tang
 * Haoyuan Liu
 */

/* Unix headers */
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <utime.h>
#include <fcntl.h>
#include <setjmp.h>
#include <netdb.h>
#ifndef WIN32
#include <sys/mman.h>
#endif

/* For libusb */
#include <usb.h>

/* standard C libraries */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>

/* User defined headers */
#include "config.h"
#include "ptp.h"
#include "protocol.h"

#ifdef WIN32
#define usleep(usec) Sleep((usec)/1000)
#define sleep(sec) Sleep(sec*1000)
#endif

#ifdef ENABLE_NLS
#  include <libintl.h>
#  undef _
#  define _(String) dgettext (GETTEXT_PACKAGE, String)
#  ifdef gettext_noop
#    define N_(String) gettext_noop (String)
#  else
#    define N_(String) (String)
#  endif
#else
#  define textdomain(String) (String)
#  define gettext(String) (String)
#  define dgettext(Domain,Message) (Message)
#  define dcgettext(Domain,Message,Type) (Message)
#  define bindtextdomain(Domain,Directory) (Domain)
#  define _(String) (String)
#  define N_(String) (String)
#endif

#include "ptpcam.h"

/* some defines comes here */

/* CHDK additions */
#define CHDKBUFS 65535
#define CHDK_MODE_INTERACTIVE 0
#define CHDK_MODE_CLI 1
#define MAXCONNRETRIES 10


/* USB interface class */
#ifndef USB_CLASS_PTP
#define USB_CLASS_PTP		6
#endif

/* USB control message data phase direction */
#ifndef USB_DP_HTD
#define USB_DP_HTD		(0x00 << 7)	/* host to device */
#endif
#ifndef USB_DP_DTH
#define USB_DP_DTH		(0x01 << 7)	/* device to host */
#endif

/* PTP class specific requests */
#ifndef USB_REQ_DEVICE_RESET
#define USB_REQ_DEVICE_RESET		0x66
#endif
#ifndef USB_REQ_GET_DEVICE_STATUS
#define USB_REQ_GET_DEVICE_STATUS	0x67
#endif

/* USB Feature selector HALT */
#ifndef USB_FEATURE_HALT
#define USB_FEATURE_HALT	0x00
#endif

/* OUR APPLICATION USB URB (2MB) ;) */
#define PTPCAM_USB_URB		2097152

#define USB_TIMEOUT		5000
#define USB_CAPTURE_TIMEOUT	20000

#define GPSFILE		"./GPS.txt"
#define GPSMESSAGE	"should be substituted by real GPS info\n"

/* one global variable (yes, I know it sucks) */
short verbose=0;
/* the other one, it sucks definitely ;) */
int ptpcam_usb_timeout = USB_TIMEOUT;

/* we need it for a proper signal handling :/ */
PTPParams* globalparams;

/* Global-Local Variables */
static int camera_bus = 0;		
static int camera_dev = 0;
static int camera_force = 0;
static PTP_USB ptp_usb;
static PTPParams params;
static struct usb_device *dev;
static int connected = 0;	
static int clientfd;			//for server client communication
static int gpsfd;				//for gps information storage
//static sigjmp_buf env;			//for taking picture with time lapse

extern int gIsShooting;

static void reset_connection();
void change_mode(int camera_bus, int camera_dev, short camera_force, char* buf);

void
usage()
{
	printf("USAGE: ptpcam [OPTION]\n\n");
}

/*
void
help()
{
	printf("USAGE: ptpcam [OPTION]\n\n");
	printf("Options:\n"
	"  --bus=BUS-NUMBER             USB bus number\n"
	"  --dev=DEV-NUMBER             USB assigned device number\n"
	"  -r, --reset                  Reset the device\n"
	"  -l, --list-devices           List all PTP devices\n"
	"  -i, --info                   Show device info\n"
	"  -o, --list-operations        List supported operations\n"
	"  -p, --list-properties        List all PTP device properties\n"
	"                               "
				"(e.g. focus mode, focus distance, etc.)\n"
	"  -s, --show-property=NUMBER   Display property details "
					"(or set its value,\n"
	"                               if used in conjunction with --val)\n"
	"  --set-property=NUMBER        Set property value (--val required)\n"
	"  --set=PROP-NAME              Set property by name (abbreviations allowed)\n"
	"  --val=VALUE                  Property value (numeric for --set-property and\n"
	"                               string or numeric for --set)\n"
	"  --show-all-properties        Show all properties values\n"
	"  --show-unknown-properties    Show unknown properties values\n"
	"  -L, --list-files             List all files\n"
	"  -g, --get-file=HANDLE        Get file by given handler\n"
	"  -G, --get-all-files          Get all files\n"
	"  --overwrite                  Force file overwrite while saving"
					"to disk\n"
	"  -d, --delete-object=HANDLE   Delete object (file) by given handle\n"
	"  -D, --delete-all-files       Delete all files form camera\n"
	"  -c, --capture                Initiate capture\n"
	"  --nikon-ic, --nic            Initiate Nikon Direct Capture (no download!)\n"
	"  --nikon-dc, --ndc            Initiate Nikon Direct Capture and download\n"
	"  --loop-capture=N             Perform N times capture/get/delete\n"
	"  -f, --force                  Talk to non PTP devices\n"
	"  -v, --verbose                Be verbose (print more debug)\n"
	"  -h, --help                   Print this help message\n"
	"  --chdk[=command]             CHDK mode. Interactive shell unless optional\n"
	"                               command is given. Run interactive shell and\n"
	"                               press 'h' for a list of commands.\n"
	"\n");
}
*/

/*******************************************************************************
* Function     : ptpcam_siginthandler                                          *
* Parameters   : signum -- the number of the signal to handle                  *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   On sigint, close the camera by calling the close_camera function and quit. * 
*******************************************************************************/
void
ptpcam_siginthandler(int signum)
{
	if (globalparams == NULL) exit(0);
    PTP_USB* ptp_usb=(PTP_USB *)globalparams->data;
	if (ptp_usb == NULL) exit(0);
    struct usb_device *dev=usb_device(ptp_usb->handle);

    if (signum==SIGINT)
    {
	/* hey it's not that easy though... but at least we can try! */
	printf("Got SIGINT, trying to clean up and close...\n");
	usleep(5000);
	if (dev != NULL) close_camera (ptp_usb, globalparams, dev);
	close(gpsfd);
	exit (-1);
    }
}

/*******************************************************************************
* Function     : ptp_read_func                                                 *
* Parameters   : bytes -- data to read                                         *
*                size -- size of data to read                                  *
*                data -- usb device handle                                     *
* Return value : PTP_RC_OK on success, PTP_ERROR_IO on failure                 *
* Functionality:                                                               *
*   By calling the USB_BULK_READ function iteratively, this function reads data*
*   from the usb device and stores into bytes.                                 *
*   Implements the read function of the PTPParam structure.                    *
*******************************************************************************/
static short
ptp_read_func (unsigned char *bytes, unsigned int size, void *data)
{
	int result=-1;
	PTP_USB *ptp_usb=(PTP_USB *)data;
	int toread=0;
	signed long int rbytes=size;

	//each time, reads PTPCAM_USB_URB number of bytes until all the data 
	//are read or the return value indicates an error 
	do {
		bytes+=toread;
		if (rbytes>PTPCAM_USB_URB) 
			toread = PTPCAM_USB_URB;
		else
			toread = rbytes;
		result=USB_BULK_READ(ptp_usb->handle, ptp_usb->inep,(char *)bytes, toread,ptpcam_usb_timeout);
		// sometimes retry might help
		if (result==0)
			result=USB_BULK_READ(ptp_usb->handle, ptp_usb->inep,(char *)bytes, toread,ptpcam_usb_timeout);
		if (result < 0)
			break;
		rbytes-=PTPCAM_USB_URB;
	} while (rbytes>0);

	//set return code and return
	if (result >= 0) {
		return (PTP_RC_OK);
	}
	else 
	{
		if (verbose) perror("usb_bulk_read");
		return PTP_ERROR_IO;
	}
}

/*******************************************************************************
* Function     : ptp_write_func                                                *
* Parameters   : bytes -- data to write                                        *
*                size -- size of data to read                                  *
*                data -- usb device handle                                     *
* Return value : PTP_RC_OK on success, PTP_ERROR_IO on failure                 *
* Functionality:                                                               *
*   By calling the USB_BULK_WRITE function this function writes data           *
*   storeed in bytes into the usb device                                       *
*   Implements the write function of the PTPParam structure.                   *
*******************************************************************************/
static short
ptp_write_func (unsigned char *bytes, unsigned int size, void *data)
{
	int result;
	PTP_USB *ptp_usb=(PTP_USB *)data;

	result=USB_BULK_WRITE(ptp_usb->handle,ptp_usb->outep,(char *)bytes,size,ptpcam_usb_timeout);
	if (result >= 0)
		return (PTP_RC_OK);
	else 
	{
		if (verbose) perror("usb_bulk_write");
		return PTP_ERROR_IO;
	}
}

/* XXX this one is suposed to return the number of bytes read!!! */
/* Implements the write function of the PTPParam structure.      */
static short
ptp_check_int (unsigned char *bytes, unsigned int size, void *data)
{
	int result;
	PTP_USB *ptp_usb=(PTP_USB *)data;

	result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)bytes,size,ptpcam_usb_timeout);
	if (result==0)
	    result=USB_BULK_READ(ptp_usb->handle, ptp_usb->intep,(char *)bytes,size,ptpcam_usb_timeout);
	if (verbose>2) fprintf (stderr, "USB_BULK_READ returned %i, size=%i\n", result, size);

	if (result >= 0) {
		return result;
	} else {
		if (verbose) perror("ptp_check_int");
		return result;
	}
}


/*******************************************************************************
* Function     : ptpcam_debug                                                  *
* Parameters   : format -- the format string of the printf function family     *
*                args -- the arguments of the format string                    *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   Implements the debug function of the PTPParam structure                    *
*******************************************************************************/
void
ptpcam_debug (void *data, const char *format, va_list args);
void
ptpcam_debug (void *data, const char *format, va_list args)
{
	if (verbose<2) return;
	vfprintf (stderr, format, args);
	fprintf (stderr,"\n");
	fflush(stderr);
}

/*******************************************************************************
* Function     : ptpcam_error                                                  *
* Parameters   : format -- the format string of the printf function family     *
*                args -- the arguments of the format string                    *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   Implements the error function of the PTPParam structure                    *
*******************************************************************************/
void
ptpcam_error (void *data, const char *format, va_list args);
void
ptpcam_error (void *data, const char *format, va_list args)
{
/*	if (!verbose) return; */
	vfprintf (stderr, format, args);
	fprintf (stderr,"\n");
	fflush(stderr);
}

/*******************************************************************************
* Function     : init_ptp_usb                                                  *
* Parameters   : param -- the PTPParam data structure to be initialized        *
*                ptp_usb -- the PTP_USB data structure to be initialized       *
*                dev -- the usb_device structure contain information used      *
*                for initialization                                            *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   Implements the error function of the PTPParam structure                    *
*******************************************************************************/
void
init_ptp_usb (PTPParams* params, PTP_USB* ptp_usb, struct usb_device* dev)
{
	usb_dev_handle *device_handle;

	params->write_func=ptp_write_func;
	params->read_func=ptp_read_func;
	params->check_int_func=ptp_check_int;
	params->check_int_fast_func=ptp_check_int;
	params->error_func=ptpcam_error;
	params->debug_func=ptpcam_debug;
	params->sendreq_func=ptp_usb_sendreq;
	params->senddata_func=ptp_usb_senddata;
	params->getresp_func=ptp_usb_getresp;
	params->getdata_func=ptp_usb_getdata;
	params->data=ptp_usb;
	params->transaction_id=0;
	params->byteorder = PTP_DL_LE;

	if ((device_handle=usb_open(dev))){
		if (!device_handle) {
			perror("usb_open()");
			close(gpsfd);
			exit(0);
		}
		ptp_usb->handle=device_handle;
		usb_set_configuration(device_handle, dev->config->bConfigurationValue);
		usb_claim_interface(device_handle,
			dev->config->interface->altsetting->bInterfaceNumber);
	}
	globalparams=params;
}

void
clear_stall(PTP_USB* ptp_usb)
{
	uint16_t status=0;
	int ret;

	/* check the inep status */
	ret=usb_get_endpoint_status(ptp_usb,ptp_usb->inep,&status);
	if (ret<0) perror ("inep: usb_get_endpoint_status()");
	/* and clear the HALT condition if happend */
	else if (status) {
		printf("Resetting input pipe!\n");
		ret=usb_clear_stall_feature(ptp_usb,ptp_usb->inep);
        	/*usb_clear_halt(ptp_usb->handle,ptp_usb->inep); */
		if (ret<0)perror ("usb_clear_stall_feature()");
	}
	status=0;

	/* check the outep status */
	ret=usb_get_endpoint_status(ptp_usb,ptp_usb->outep,&status);
	if (ret<0) perror ("outep: usb_get_endpoint_status()");
	/* and clear the HALT condition if happend */
	else if (status) {
		printf("Resetting output pipe!\n");
        	ret=usb_clear_stall_feature(ptp_usb,ptp_usb->outep);
		/*usb_clear_halt(ptp_usb->handle,ptp_usb->outep); */
		if (ret<0)perror ("usb_clear_stall_feature()");
	}

        /*usb_clear_halt(ptp_usb->handle,ptp_usb->intep); */
}

void
close_usb(PTP_USB* ptp_usb, struct usb_device* dev)
{
	//clear_stall(ptp_usb);
        usb_release_interface(ptp_usb->handle,
                dev->config->interface->altsetting->bInterfaceNumber);
	usb_reset(ptp_usb->handle);
        usb_close(ptp_usb->handle);
}


struct usb_bus*
init_usb()
{
	usb_init();
	usb_find_busses();
	usb_find_devices();
	return (usb_get_busses());
}

/*
   find_device() returns the pointer to a usb_device structure matching
   given busn, devicen numbers. If any or both of arguments are 0 then the
   first matching PTP device structure is returned. 
*/
struct usb_device*
find_device (int busn, int devicen, short force);
struct usb_device*
find_device (int busn, int devn, short force)
{
	struct usb_bus *bus;
	struct usb_device *dev;

	bus=init_usb();
	for (; bus; bus = bus->next)
	for (dev = bus->devices; dev; dev = dev->next)
	if (dev->config)
	if ((dev->config->interface->altsetting->bInterfaceClass==
		USB_CLASS_PTP)||force)
	if (dev->descriptor.bDeviceClass!=USB_CLASS_HUB)
	{
		int curbusn, curdevn;

		curbusn=strtol(bus->dirname,NULL,10);
		curdevn=strtol(dev->filename,NULL,10);

		if (devn==0) {
			if (busn==0) return dev;
			if (curbusn==busn) return dev;
		} else {
			if ((busn==0)&&(curdevn==devn)) return dev;
			if ((curbusn==busn)&&(curdevn==devn)) return dev;
		}
	}
	return NULL;
}

void
find_endpoints(struct usb_device *dev, int* inep, int* outep, int* intep);
void
find_endpoints(struct usb_device *dev, int* inep, int* outep, int* intep)
{
	int i,n;
	struct usb_endpoint_descriptor *ep;

	ep = dev->config->interface->altsetting->endpoint;
	n=dev->config->interface->altsetting->bNumEndpoints;

	for (i=0;i<n;i++) {
	if (ep[i].bmAttributes==USB_ENDPOINT_TYPE_BULK)	{
		if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==
			USB_ENDPOINT_DIR_MASK)
		{
			*inep=ep[i].bEndpointAddress;
			if (verbose>1)
				fprintf(stderr, "Found inep: 0x%02x\n",*inep);
		}
		if ((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==0)
		{
			*outep=ep[i].bEndpointAddress;
			if (verbose>1)
				fprintf(stderr, "Found outep: 0x%02x\n",*outep);
		}
		} else if ((ep[i].bmAttributes==USB_ENDPOINT_TYPE_INTERRUPT) &&
			((ep[i].bEndpointAddress&USB_ENDPOINT_DIR_MASK)==
				USB_ENDPOINT_DIR_MASK))
		{
			*intep=ep[i].bEndpointAddress;
			if (verbose>1)
				fprintf(stderr, "Found intep: 0x%02x\n",*intep);
		}
	}
}

/*******************************************************************************
* Function     : open_camera                                                   *
* Parameters   : busn -- the usb bus number                                    *
*                devn -- the usb device number                                 *
*                force -- the force flag                                       *
* Return value : 0 on success, -1 on failure                                   *
* Functionality:                                                               *
*   Find the camera device and initialize it using the init_ptp_usb function   * 
*******************************************************************************/
int
open_camera (int busn, int devn, short force, PTP_USB *ptp_usb, PTPParams *params, struct usb_device **dev)
{
	int retrycnt=0;
	uint16_t ret=0;

#ifdef DEBUG
	printf("dev %i\tbus %i\n",devn,busn);
#endif
	
  // retry device find for a while (in case the user just powered it on or called restart)
	while ((retrycnt++ < MAXCONNRETRIES) && !ret) {
		*dev=find_device(busn,devn,force);
		if (*dev!=NULL) 
			ret=1;
		else {
			fprintf(stderr,"Could not find any device matching given bus/dev numbers, retrying in 1 s...\n");
			fflush(stderr);
			sleep(1);
		}
	}

	if (*dev==NULL) {
		fprintf(stderr,"could not find any device matching given "
		"bus/dev numbers\n");
		return -1;
	}
	find_endpoints(*dev,&ptp_usb->inep,&ptp_usb->outep,&ptp_usb->intep);
    init_ptp_usb(params, ptp_usb, *dev);   

  // first connection attempt often fails if some other app or driver has accessed the camera, retry for a while
	retrycnt=0;
	while ((retrycnt++ < MAXCONNRETRIES) && ((ret=ptp_opensession(params,1))!=PTP_RC_OK)) {
		printf("Failed to connect (attempt %d), retrying in 1 s...\n", retrycnt);
		close_usb(ptp_usb, *dev);
		sleep(1);
		find_endpoints(*dev,&ptp_usb->inep,&ptp_usb->outep,&ptp_usb->intep);
		init_ptp_usb(params, ptp_usb, *dev);   
	}  
	if (ret != PTP_RC_OK) {
		fprintf(stderr,"ERROR: Could not open session!\n");
		close_usb(ptp_usb, *dev);
		return -1;
	}

	if (ptp_getdeviceinfo(params,&params->deviceinfo)!=PTP_RC_OK) {
		fprintf(stderr,"ERROR: Could not get device info!\n");
		close_usb(ptp_usb, *dev);
		return -1;
	}
	return 0;
}

/*******************************************************************************
* Function     : close_camera                                                  *
* Parameters   : ptp_usb -- the data structure holding the device handle       * 
*                params -- the PTPParameter holding all the information        *
*                dev -- the device to close                                    *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   Close the usb device                                                       *
*******************************************************************************/
void
close_camera (PTP_USB *ptp_usb, PTPParams *params, struct usb_device *dev)
{
	if (ptp_closesession(params)!=PTP_RC_OK)
		fprintf(stderr,"ERROR: Could not close session!\n");
	close_usb(ptp_usb, dev);
}


//void
//list_devices(short force)
//{
//	struct usb_bus *bus;
//	struct usb_device *dev;
//	int found=0;
//
//
//	bus=init_usb();
//  	for (; bus; bus = bus->next)
//    	for (dev = bus->devices; dev; dev = dev->next) {
//		/* if it's a PTP device try to talk to it */
//		if (dev->config)
//		if ((dev->config->interface->altsetting->bInterfaceClass==
//			USB_CLASS_PTP)||force)
//		if (dev->descriptor.bDeviceClass!=USB_CLASS_HUB)
//		{
//			PTPParams params;
//			PTP_USB ptp_usb;
//			PTPDeviceInfo deviceinfo;
//
//			if (!found){
//				printf("\nListing devices...\n");
//				printf("bus/dev\tvendorID/prodID\tdevice model\n");
//				found=1;
//			}
//
//			find_endpoints(dev,&ptp_usb.inep,&ptp_usb.outep,
//				&ptp_usb.intep);
//			init_ptp_usb(&params, &ptp_usb, dev);
//
//			CC(ptp_opensession (&params,1),
//				"Could not open session!\n"
//				"Try to reset the camera.\n");
//			CC(ptp_getdeviceinfo (&params, &deviceinfo),
//				"Could not get device info!\n");
//
//      			printf("%s/%s\t0x%04X/0x%04X\t%s\n",
//				bus->dirname, dev->filename,
//				dev->descriptor.idVendor,
//				dev->descriptor.idProduct, deviceinfo.Model);
//
//			CC(ptp_closesession(&params),
//				"Could not close session!\n");
//			close_usb(&ptp_usb, dev);
//		}
//	}
//	if (!found) printf("\nFound no PTP devices\n");
//	printf("\n");
//}

//void
//show_info (int busn, int devn, short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//
//	printf("\nCamera information\n");
//	printf("==================\n");
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//	printf("Model: %s\n",params.deviceinfo.Model);
//	printf("  manufacturer: %s\n",params.deviceinfo.Manufacturer);
//	printf("  serial number: '%s'\n",params.deviceinfo.SerialNumber);
//	printf("  device version: %s\n",params.deviceinfo.DeviceVersion);
//	printf("  extension ID: 0x%08lx\n",(long unsigned)
//					params.deviceinfo.VendorExtensionID);
//	printf("  extension description: %s\n",
//					params.deviceinfo.VendorExtensionDesc);
//	printf("  extension version: 0x%04x\n",
//				params.deviceinfo.VendorExtensionVersion);
//	printf("\n");
//	close_camera(&ptp_usb, &params, dev);
//}

//void
//capture_image (int busn, int devn, short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	PTPContainer event;
//	int ExposureTime=0;
//	struct usb_device *dev;
//	short ret;
//
//	printf("\nInitiating captue...\n");
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	if (!ptp_operation_issupported(&params, PTP_OC_InitiateCapture))
//	{
//	    printf ("Your camera does not support InitiateCapture operation!\nSorry, blame the %s!\n", params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	/* obtain exposure time in miliseconds */
//	if (ptp_property_issupported(&params, PTP_DPC_ExposureTime))
//	{
//	    PTPDevicePropDesc dpd;
//	    memset(&dpd,0,sizeof(dpd));
//	    ret=ptp_getdevicepropdesc(&params,PTP_DPC_ExposureTime,&dpd);
//	    if (ret==PTP_RC_OK) ExposureTime=(*(int32_t*)(dpd.CurrentValue))/10;
//	}
//
//	/* adjust USB timeout */
//	if (ExposureTime>USB_TIMEOUT) ptpcam_usb_timeout=ExposureTime;
//
//	CR(ptp_initiatecapture (&params, 0x0, 0), "Could not capture.\n");
//	
//	ret=ptp_usb_event_wait(&params,&event);
//	if (ret!=PTP_RC_OK) goto err;
//	if (verbose) printf ("Event received %08x, ret=%x\n", event.Code, ret);
//	if (event.Code==PTP_EC_CaptureComplete) {
//		printf ("Camera reported 'capture completed' but the object information is missing.\n");
//		goto out;
//	}
//		
//	while (event.Code==PTP_EC_ObjectAdded) {
//		printf ("Object added 0x%08lx\n", (long unsigned) event.Param1);
//		if (ptp_usb_event_wait(&params, &event)!=PTP_RC_OK)
//			goto err;
//		if (verbose) printf ("Event received %08x, ret=%x\n", event.Code, ret);
//		if (event.Code==PTP_EC_CaptureComplete) {
//			printf ("Capture completed successfully!\n");
//			goto out;
//		}
//	}
//	
//err:
//	printf("Events receiving error. Capture status unknown.\n");
//out:
//
//	ptpcam_usb_timeout=USB_TIMEOUT;
//	close_camera(&ptp_usb, &params, dev);
//}

//void
//loop_capture (int busn, int devn, short force, int n,  int overwrite)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	PTPContainer event;
//	struct usb_device *dev;
//	int file;
//	PTPObjectInfo oi;
//	uint32_t handle=0;
//	char *image;
//	int ret;
//	char *filename;
//
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	/* capture timeout should be longer */
//	ptpcam_usb_timeout=USB_CAPTURE_TIMEOUT;
//
//	printf("Camera: %s\n",params.deviceinfo.Model);
//
//
//	/* local loop */
//	while (n>0) {
//		/* capture */
//		printf("\nInitiating captue...\n");
//		CR(ptp_initiatecapture (&params, 0x0, 0),"Could not capture\n");
//		n--;
//
//		ret=ptp_usb_event_wait(&params,&event);
//		if (verbose) printf ("Event received %08x, ret=%x\n", event.Code, ret);
//		if (ret!=PTP_RC_OK) goto err;
//		if (event.Code==PTP_EC_CaptureComplete) {
//			printf ("CANNOT DOWNLOAD: got 'capture completed' but the object information is missing.\n");
//			goto out;
//		}
//			
//		while (event.Code==PTP_EC_ObjectAdded) {
//			printf ("Object added 0x%08lx\n",(long unsigned) event.Param1);
//			handle=event.Param1;
//			if (ptp_usb_event_wait(&params, &event)!=PTP_RC_OK)
//				goto err;
//			if (verbose) printf ("Event received %08x, ret=%x\n", event.Code, ret);
//			if (event.Code==PTP_EC_CaptureComplete)
//				goto download;
//		}
//download:	
//
//		memset(&oi, 0, sizeof(PTPObjectInfo));
//		if (verbose) printf ("Downloading: 0x%08lx\n",(long unsigned) handle);
//		if ((ret=ptp_getobjectinfo(&params,handle, &oi))!=PTP_RC_OK){
//			fprintf(stderr,"ERROR: Could not get object info\n");
//			ptp_perror(&params,ret);
//			if (ret==PTP_ERROR_IO) clear_stall(&ptp_usb);
//			continue;
//		}
//	
//		if (oi.ObjectFormat == PTP_OFC_Association)
//				goto out;
//		filename=(oi.Filename);
//#ifdef WIN32
//                goto out;
//#else
//		file=open(filename, (overwrite==OVERWRITE_EXISTING?0:O_EXCL)|O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP);
//#endif
//		if (file==-1) {
//			if (errno==EEXIST) {
//				printf("Skipping file: \"%s\", file exists!\n",filename);
//				goto out;
//			}
//			perror("open");
//			goto out;
//		}
//		lseek(file,oi.ObjectCompressedSize-1,SEEK_SET);
//		ret=write(file,"",1);
//		if (ret==-1) {
//			perror("write");
//			goto out;
//		}
//#ifndef WIN32
//		image=mmap(0,oi.ObjectCompressedSize,PROT_READ|PROT_WRITE,MAP_SHARED,
//			file,0);
//		if (image==MAP_FAILED) {
//			perror("mmap");
//			close(file);
//			goto out;
//		}
//#endif
//		printf ("Saving file: \"%s\" ",filename);
//		fflush(NULL);
//		ret=ptp_getobject(&params,handle,&image);
//		munmap(image,oi.ObjectCompressedSize);
//		close(file);
//		if (ret!=PTP_RC_OK) {
//			printf ("error!\n");
//			ptp_perror(&params,ret);
//			if (ret==PTP_ERROR_IO) clear_stall(&ptp_usb);
//		} else {
//			/* and delete from camera! */
//			printf("is done...\nDeleting from camera.\n");
//			CR(ptp_deleteobject(&params, handle,0),
//					"Could not delete object\n");
//			printf("Object 0x%08lx (%s) deleted.\n",(long unsigned) handle, oi.Filename);
//		}
//out:
//		;
//	}
//err:
//
//	ptpcam_usb_timeout=USB_TIMEOUT;
//	close_camera(&ptp_usb, &params, dev);
//}

//void
//nikon_initiate_dc (int busn, int devn, short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	uint16_t result;
//    
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	printf("Camera: %s\n",params.deviceinfo.Model);
//	printf("\nInitiating direct captue...\n");
//	
//	if (params.deviceinfo.VendorExtensionID!=PTP_VENDOR_NIKON)
//	{
//	    printf ("Your camera is not Nikon!\nDo not buy from %s!\n",params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	if (!ptp_operation_issupported(&params,PTP_OC_NIKON_DirectCapture)) {
//	    printf ("Sorry, your camera dows not support Nikon DirectCapture!\nDo not buy from %s!\n",params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	/* perform direct capture */
//	result=ptp_nikon_directcapture (&params, 0xffffffff);
//	if (result!=PTP_RC_OK) {
//	    ptp_perror(&params,result);
//	    fprintf(stderr,"ERROR: Could not capture.\n");
//	    if (result!=PTP_RC_StoreFull) {
//		close_camera(&ptp_usb, &params, dev);
//		return;
//	    }
//	}
//	usleep(300*1000);
//out:	
//	close_camera(&ptp_usb, &params, dev);
//
//}

//void
//nikon_direct_capture (int busn, int devn, short force, char* filename,int overwrite)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	uint16_t result;
//	uint16_t nevent=0;
//	PTPUSBEventContainer* events=NULL;
//	int ExposureTime=0;	/* exposure time in miliseconds */
//	int BurstNumber=1;
//	PTPDevicePropDesc dpd;
//	PTPObjectInfo oi;
//	int i;
//    
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	printf("Camera: %s\n",params.deviceinfo.Model);
//
//	if ((result=ptp_getobjectinfo(&params,0xffff0001, &oi))==PTP_RC_OK) {
//	    if (filename==NULL) filename=oi.Filename;
//	    save_object(&params, 0xffff0001, filename, oi, overwrite);
//	    goto out;
//	}
//
//	printf("\nInitiating direct captue...\n");
//	
//	if (params.deviceinfo.VendorExtensionID!=PTP_VENDOR_NIKON)
//	{
//	    printf ("Your camera is not Nikon!\nDo not buy from %s!\n",params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	if (!ptp_operation_issupported(&params,PTP_OC_NIKON_DirectCapture)) {
//	    printf ("Sorry, your camera dows not support Nikon DirectCapture!\nDo not buy from %s!\n",params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	/* obtain exposure time in miliseconds */
//	memset(&dpd,0,sizeof(dpd));
//	result=ptp_getdevicepropdesc(&params,PTP_DPC_ExposureTime,&dpd);
//	if (result==PTP_RC_OK) ExposureTime=(*(int32_t*)(dpd.CurrentValue))/10;
//
//	/* obtain burst number */
//	memset(&dpd,0,sizeof(dpd));
//	result=ptp_getdevicepropdesc(&params,PTP_DPC_BurstNumber,&dpd);
//	if (result==PTP_RC_OK) BurstNumber=*(uint16_t*)(dpd.CurrentValue);
///*
//	if ((result=ptp_getobjectinfo(&params,0xffff0001, &oi))==PTP_RC_OK)
//	{
//	    if (filename==NULL) filename=oi.Filename;
//	    save_object(&params, 0xffff0001, filename, oi, overwrite);
//	    ptp_nikon_keepalive(&params);
//	    ptp_nikon_keepalive(&params);
//	    ptp_nikon_keepalive(&params);
//	    ptp_nikon_keepalive(&params);
//	}
//*/
//
//	/* perform direct capture */
//	result=ptp_nikon_directcapture (&params, 0xffffffff);
//	if (result!=PTP_RC_OK) {
//	    ptp_perror(&params,result);
//	    fprintf(stderr,"ERROR: Could not capture.\n");
//	    if (result!=PTP_RC_StoreFull) {
//		close_camera(&ptp_usb, &params, dev);
//		return;
//	    }
//	}
//	if (BurstNumber>1) printf("Capturing %i frames in burst.\n",BurstNumber);
//
//	/* sleep in case of exposure longer than 1/100 */
//	if (ExposureTime>10) {
//	    printf ("sleeping %i miliseconds\n", ExposureTime);
//	    usleep (ExposureTime*1000);
//	}
//
//	while (BurstNumber>0) {
//
//#if 0	    /* Is this really needed??? */
//	    ptp_nikon_keepalive(&params);
//#endif
//
//	    result=ptp_nikon_checkevent (&params, &events, &nevent);
//	    if (result != PTP_RC_OK) {
//		fprintf(stderr, "Error checking Nikon events\n");
//		ptp_perror(&params,result);
//		goto out;
//	    }
//	    for(i=0;i<nevent;i++) {
//	    ptp_nikon_keepalive(&params);
//		void *prop;
//		if (events[i].code==PTP_EC_DevicePropChanged) {
//		    printf ("Checking: %s\n", ptp_prop_getname(&params, events[i].param1));
//		    ptp_getdevicepropvalue(&params, events[i].param1, &prop, PTP_DTC_UINT64);
//		}
//
//		printf("Event [%i] = 0x%04x,\t param: %08x\n",i, events[i].code ,events[i].param1);
//		if (events[i].code==PTP_EC_NIKON_CaptureOverflow) {
//		    printf("Ram cache overflow? Shooting to fast!\n");
//		    if ((result=ptp_getobjectinfo(&params,0xffff0001, &oi))!=PTP_RC_OK) {
//		        fprintf(stderr, "Could not get object info\n");
//		        ptp_perror(&params,result);
//		        goto out;
//		    }
//		    if (filename==NULL) filename=oi.Filename;
//		    save_object(&params, 0xffff0001, filename, oi, overwrite);
//		    BurstNumber=0;
//		    usleep(100);
//		} else
//		if (events[i].code==PTP_EC_NIKON_ObjectReady) 
//		{
//		    if ((result=ptp_getobjectinfo(&params,0xffff0001, &oi))!=PTP_RC_OK) {
//		        fprintf(stderr, "Could not get object info\n");
//		        ptp_perror(&params,result);
//		        goto out;
//		    }
//		    if (filename==NULL) filename=oi.Filename;
//		    save_object(&params, 0xffff0001, filename, oi, overwrite);
//		    BurstNumber--;
//		}
//	    }
//	    free (events);
//	}
//
//out:	
//	ptpcam_usb_timeout=USB_TIMEOUT;
//	close_camera(&ptp_usb, &params, dev);
//}


//void
//nikon_direct_capture2 (int busn, int devn, short force, char* filename, int overwrite)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	uint16_t result;
//	PTPObjectInfo oi;
//
//	dev=find_device(busn,devn,force);
//	if (dev==NULL) {
//		fprintf(stderr,"could not find any device matching given "
//		"bus/dev numbers\n");
//		close(gpsfd);
//		exit(-1);
//	}
//	find_endpoints(dev,&ptp_usb.inep,&ptp_usb.outep,&ptp_usb.intep);
//
//	init_ptp_usb(&params, &ptp_usb, dev);
//
//	if (ptp_opensession(&params,1)!=PTP_RC_OK) {
//		fprintf(stderr,"ERROR: Could not open session!\n");
//		close_usb(&ptp_usb, dev);
//		return ;
//	}
///*
//	memset(&dpd,0,sizeof(dpd));
//	result=ptp_getdevicepropdesc(&params,PTP_DPC_BurstNumber,&dpd);
//	memset(&dpd,0,sizeof(dpd));
//	result=ptp_getdevicepropdesc(&params,PTP_DPC_ExposureTime,&dpd);
//*/
//
//	/* perform direct capture */
//	result=ptp_nikon_directcapture (&params, 0xffffffff);
//	if (result!=PTP_RC_OK) {
//	    ptp_perror(&params,result);
//	    fprintf(stderr,"ERROR: Could not capture.\n");
//	    if (result!=PTP_RC_StoreFull) {
//	        close_camera(&ptp_usb, &params, dev);
//	        return;
//	    }
//	}
//
//	if (ptp_closesession(&params)!=PTP_RC_OK)
//	{
//	    fprintf(stderr,"ERROR: Could not close session!\n");
//	    return;
//	}
//
//	usleep(300*1000);
//
//	if (ptp_opensession(&params,1)!=PTP_RC_OK) {
//    		fprintf(stderr,"ERROR: Could not open session!\n");
//    		close_usb(&ptp_usb, dev);
//    		return;
//    	}
//loop:
//    	if ((result=ptp_getobjectinfo(&params,0xffff0001, &oi))==PTP_RC_OK) {
//    	    if (filename==NULL) filename=oi.Filename;
//    	    save_object(&params, 0xffff0001, filename, oi, overwrite);
//    	} else {
//	    ptp_nikon_keepalive(&params);
//	    goto loop;
//	}
//
///*out:	*/
//	close_camera(&ptp_usb, &params, dev);
//
//
//#if 0
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	uint16_t result;
//	uint16_t nevent=0;
//	PTPUSBEventContainer* events=NULL;
//	int ExposureTime=0;	/* exposure time in miliseconds */
//	int BurstNumber=1;
//	PTPDevicePropDesc dpd;
//	PTPObjectInfo oi;
//	int i;
//	char *filename=NULL;
//    
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	printf("Camera: %s\n",params.deviceinfo.Model);
//	printf("\nInitiating direct captue...\n");
//	
//	if (params.deviceinfo.VendorExtensionID!=PTP_VENDOR_NIKON)
//	{
//	    printf ("Your camera is not Nikon!\nDo not buy from %s!\n",params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	if (!ptp_operation_issupported(&params,PTP_OC_NIKON_DirectCapture)) {
//	    printf ("Sorry, your camera dows not support Nikon DirectCapture!\nDo not buy from %s!\n",params.deviceinfo.Manufacturer);
//	    goto out;
//	}
//
//	/* obtain exposure time in miliseconds */
//	memset(&dpd,0,sizeof(dpd));
//	result=ptp_getdevicepropdesc(&params,PTP_DPC_ExposureTime,&dpd);
//	if (result==PTP_RC_OK) ExposureTime=(*(int32_t*)(dpd.CurrentValue))/10;
//
//	/* obtain burst number */
//	memset(&dpd,0,sizeof(dpd));
//	result=ptp_getdevicepropdesc(&params,PTP_DPC_BurstNumber,&dpd);
//	if (result==PTP_RC_OK) BurstNumber=*(uint16_t*)(dpd.CurrentValue);
//
//	if (BurstNumber>1) printf("Capturing %i frames in burst.\n",BurstNumber);
//#if 0
//	/* sleep in case of exposure longer than 1/100 */
//	if (ExposureTime>10) {
//	    printf ("sleeping %i miliseconds\n", ExposureTime);
//	    usleep (ExposureTime*1000);
//	}
//#endif
//
//	while (num>0) {
//	    /* perform direct capture */
//	    result=ptp_nikon_directcapture (&params, 0xffffffff);
//	    if (result!=PTP_RC_OK) {
//	        if (result==PTP_ERROR_IO) {
//	    	close_camera(&ptp_usb, &params, dev);
//	    	return;
//	        }
//	    }
//
//#if 0	    /* Is this really needed??? */
//	    ptp_nikon_keepalive(&params);
//#endif
//	    ptp_nikon_keepalive(&params);
//	    ptp_nikon_keepalive(&params);
//	    ptp_nikon_keepalive(&params);
//	    ptp_nikon_keepalive(&params);
//
//	    result=ptp_nikon_checkevent (&params, &events, &nevent);
//	    if (result != PTP_RC_OK) goto out;
//	    
//	    for(i=0;i<nevent;i++) {
//		printf("Event [%i] = 0x%04x,\t param: %08x\n",i, events[i].code ,events[i].param1);
//		if (events[i].code==PTP_EC_NIKON_ObjectReady) 
//		{
//		    num--;
//		    if ((result=ptp_getobjectinfo(&params,0xffff0001, &oi))!=PTP_RC_OK) {
//		        fprintf(stderr, "Could not get object info\n");
//		        ptp_perror(&params,result);
//		        goto out;
//		    }
//		    if (filename==NULL) filename=oi.Filename;
//		    save_object(&params, 0xffff0001, filename, oi, overwrite);
//		}
//		if (events[i].code==PTP_EC_NIKON_CaptureOverflow) {
//		    printf("Ram cache overflow, capture terminated\n");
//		    //BurstNumber=0;
//		}
//	    }
//	    free (events);
//	}
//
//out:	
//	ptpcam_usb_timeout=USB_TIMEOUT;
//	close_camera(&ptp_usb, &params, dev);
//#endif
//}




void
list_files (int busn, int devn, short force)
{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
	int i;
	PTPObjectInfo oi;
	struct tm *tm;

	printf("\nListing files...\n");
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
	//printf("Camera: %s\n",params.deviceinfo.Model);
	CR(ptp_getobjecthandles (&params,0xffffffff, 0x000000, 0x000000,
		&params.handles),"Could not get object handles\n");
	printf("Handler:           Size: \tCaptured:      \tname:\n");
	for (i = 0; i < params.handles.n; i++) {
		CR(ptp_getobjectinfo(&params,params.handles.Handler[i],
			&oi),"Could not get object info\n");
		if (oi.ObjectFormat == PTP_OFC_Association)
			continue;
		tm=gmtime(&oi.CaptureDate);
		printf("0x%08lx: %12u\t%4i-%02i-%02i %02i:%02i\t%s\n",
			(long unsigned)params.handles.Handler[i],
			(unsigned) oi.ObjectCompressedSize, 
			tm->tm_year+1900, tm->tm_mon+1,tm->tm_mday,
			tm->tm_hour, tm->tm_min,
			oi.Filename);
	}
	printf("\n");
//	close_camera(&ptp_usb, &params, dev);
}

void
delete_object (int busn, int devn, short force, uint32_t handle)
{
	PTPParams params;
	PTP_USB ptp_usb;
	struct usb_device *dev;
	PTPObjectInfo oi;

	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
		return;
	CR(ptp_getobjectinfo(&params,handle,&oi),
		"Could not get object info\n");
	CR(ptp_deleteobject(&params, handle,0), "Could not delete object\n");
	printf("\nObject 0x%08lx (%s) deleted.\n",(long unsigned) handle, oi.Filename);
	close_camera(&ptp_usb, &params, dev);
}

void
delete_all_files (int busn, int devn, short force)
{
	PTPParams params;
	PTP_USB ptp_usb;
	struct usb_device *dev;
	PTPObjectInfo oi;
	uint32_t handle;
	int i;
	int ret;

	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
		return;
	printf("Camera: %s\n",params.deviceinfo.Model);
	CR(ptp_getobjecthandles (&params,0xffffffff, 0x000000, 0x000000,
		&params.handles),"Could not get object handles\n");

	for (i=0; i<params.handles.n; i++) {
		handle=params.handles.Handler[i];
		if ((ret=ptp_getobjectinfo(&params,handle, &oi))!=PTP_RC_OK){
			fprintf(stderr,"Handle: 0x%08lx\n",(long unsigned) handle);
			fprintf(stderr,"ERROR: Could not get object info\n");
			ptp_perror(&params,ret);
			if (ret==PTP_ERROR_IO) clear_stall(&ptp_usb);
			continue;
		}
		if (oi.ObjectFormat == PTP_OFC_Association)
			continue;
		CR(ptp_deleteobject(&params, handle,0),
				"Could not delete object\n");
		printf("Object 0x%08lx (%s) deleted.\n",(long unsigned) handle, oi.Filename);
	}
	close_camera(&ptp_usb, &params, dev);
}

/*******************************************************************************
* Function     : save_object                                                   *
* Parameters   : param -- contains information of all the object               *
*                handle -- handle points to the object                         *
*                filename -- filename of the image                             *
*                oi -- holds the information of the image                      *
*                overwriet -- whether to overwrite if the image exists         * 
* Return value : n/a                                                           *
* Functionality:                                                               *
*   For the image to save, create a image file in the DCIM directory, and      *
*   use memory mapping and ptp_get_object function to copy the content of      *
*   the image on the camera to the image file created.                         *
*******************************************************************************/
void
save_object(PTPParams *params, uint32_t handle, char* filename, PTPObjectInfo oi, int overwrite)
{
	int file;
	char *image;
	int ret;
	struct utimbuf timebuf;
	char path_to_save_file[256];

	//initialize buffer holding the path of the directory to save image
	memset(path_to_save_file, '\0', sizeof(path_to_save_file));
	strncpy(path_to_save_file, "./DCIM/", strlen("./DCIM/"));

#ifdef WIN32
        goto out;
#else
	//check if a dir for store pic exists, if not create one
	struct stat st;
	if(stat(path_to_save_file, &st) != 0 ){
		mkdir(path_to_save_file, 0777);
	}
	strncat(path_to_save_file, filename, strlen(filename));
	
	//open file to be saved
	file=open(path_to_save_file, (overwrite==OVERWRITE_EXISTING?0:O_EXCL)|O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP);
#endif
	if (file==-1) {
		if (errno==EEXIST) {
			//printf("Skipping file: \"%s\", file exists!\n",filename);
			goto out;
		}
		perror("open");
		goto out;
	}
	lseek(file,oi.ObjectCompressedSize-1,SEEK_SET);
	ret=write(file,"",1);
	if (ret==-1) {
	    perror("write");
	    goto out;
	}
#ifndef WIN32
	//do the memory mapping
	image=mmap(0,oi.ObjectCompressedSize,PROT_READ|PROT_WRITE,MAP_SHARED,
		file,0);
	if (image==MAP_FAILED) {
		perror("mmap");
		close(file);
		goto out;
	}
#endif

	//output message 
	printf ("Saving file: \"%s\" ",filename);
	fflush(NULL);

	//get object content using the ptp_getobject function
	ret=ptp_getobject(params,handle,&image);
	munmap(image,oi.ObjectCompressedSize);
	if (close(file)==-1) {
	    perror("close");
	}

	//get correct timestamp of the image
	timebuf.actime=oi.ModificationDate;
	timebuf.modtime=oi.CaptureDate;
	utime(path_to_save_file,&timebuf);
	//utime(filename,&timebuf);
	if (ret!=PTP_RC_OK) {
		printf ("error!\n");
		ptp_perror(params,ret);
		if (ret==PTP_ERROR_IO) clear_stall((PTP_USB *)(params->data));
	} else {
		printf("is done.\n");
	}
out:
	return;
}

/*******************************************************************************
* Function     : get_save_object                                               *
* Parameters   : params -- the PTPParam structure which contains information   *
*                handle -- the handle of the object to save                    *
*                filename -- the filename of the object to save                *
*                overwrite -- whether to overwrite existing object             *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   For the object described by handle, get the filename of the object and     *
*   call the save_object function to download the object if the object is a jpg*
*   file.                                                                      *
*******************************************************************************/
void
get_save_object (PTPParams *params, uint32_t handle, char* filename, int overwrite)
{

	PTPObjectInfo oi;
	int ret;

   	memset(&oi, 0, sizeof(PTPObjectInfo));
	if (verbose)
		printf ("Handle: 0x%08lx\n",(long unsigned) handle);

	//get object information from params and store information into oi
	if ((ret=ptp_getobjectinfo(params,handle, &oi))!=PTP_RC_OK) {
	    fprintf(stderr, "Could not get object info\n");
	    ptp_perror(params,ret);
	    if (ret==PTP_ERROR_IO) clear_stall((PTP_USB *)(params->data));
	    goto out;
	}
	if (oi.ObjectFormat == PTP_OFC_Association)
			goto out;

	//get filename if filename is NULL
	if (filename==NULL) filename=(oi.Filename);

	//download object if object is a jpg file
	if (strstr(filename, ".JPG") || strstr(filename, ".jpg"))
		save_object(params, handle, filename, oi, overwrite);
out:
	return;

}

void
get_file (int busn, int devn, short force, uint32_t handle, char* filename,
int overwrite)
{
	PTPParams params;
	PTP_USB ptp_usb;
	struct usb_device *dev;

	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
		return;
	printf("Camera: %s\n",params.deviceinfo.Model);

	get_save_object(&params, handle, filename, overwrite);

	close_camera(&ptp_usb, &params, dev);

}
void change_mode(int camera_bus, int camera_dev, short camera_force, char* buf)	{
	char mode[8];
	
	sscanf(buf, "%s", mode);	
	
	if(!strncmp(mode, "M", strlen(mode))){
		printf("entering M mode\n");
		
    	ptp_chdk_exec_lua("capmode=require('capmode') capmode.set(\"M\")", 0, &params, &params.deviceinfo); 
	}	
	else if(!strncmp(mode, "TV", strlen(mode))){
		printf("entering TV mode\n");
    	ptp_chdk_exec_lua("capmode=require('capmode') capmode.set(\"TV\")", 0, &params, &params.deviceinfo); 
	}	
	else if(!strncmp(mode, "AV", strlen(mode))){
		printf("entering AV mode\n");
    	ptp_chdk_exec_lua("capmode=require('capmode') capmode.set(\"AV\")", 0, &params, &params.deviceinfo); 
	}	
	else if(!strncmp(mode, "P", strlen(mode))){
		printf("entering P mode\n");
    	ptp_chdk_exec_lua("capmode=require('capmode') capmode.set(\"P\")", 0, &params, &params.deviceinfo); 
	}	
	else{
		printf("invalid mode");
	}

}

/*******************************************************************************
* Function     : timelapse                                                     *
* Parameters   : n/a                                                           *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   Take picture with the user specified time lapse until user enter stop      *
*******************************************************************************/
void timelapse(int camera_bus, int camera_dev, short camera_force, int lapse)	
{
	fd_set rfds;		//read file descriptor set for select
	char buf[CHDKBUFS];	//hold user input
	char * s = NULL;	//char read from user input
	char c = ' ';
	int pipes[2];		//1 for write end, 0 for read end
	int pid = -1;		//pid for fork
	int ret = -1;		//return value for select
	int num = 0;		//number of pictures taken

	//get user input of the time lapse between adjacent pictures
	memset(buf, 0, sizeof(char) * CHDKBUFS);
	pipe(pipes);
	//create two processes, parent to handle user input, child to take pictures
	pid = fork();
	switch(pid) {
	case -1:
		perror("fork");
		close(gpsfd);
		close(pipes[0]);
		close(pipes[1]);
		exit(EXIT_FAILURE);
	case 0:		//child
		close(pipes[0]);	//close read end, which is unused
		while(1) {
			// shoot a picture
    		if (ptp_chdk_exec_lua("get_shooting()",1,&params,&params.deviceinfo) && !gIsShooting) {
    			if(ptp_chdk_exec_lua("click(\"shoot_full\")",0,&params,&params.deviceinfo)) {
					write(gpsfd, GPSMESSAGE, strlen(GPSMESSAGE));	//write GPS information
					num++;
					if (num >= 50) {	//tell parent hardware limit exceeded, and start to sleep
						write(pipes[1], "c", 1);
						sleep(1);
						close(pipes[1]);
						while (1) sleep(1);
					}
			    }
			}
			else {
				continue;
			}
    		//ptp_chdk_exec_lua("repeat sleep(1) until get_shooting()==false shoot()",0,&params,&params.deviceinfo); 
    		//if (ptp_chdk_exec_lua("click(\"shoot_full\")",0,&params,&params.deviceinfo)) {
			//	write(gpsfd, GPSMESSAGE, strlen(GPSMESSAGE));
			//}
			sleep(lapse);
		}
		break;
	default: 	//parent
		close(pipes[1]);
		while (1) {
			printf("%s ", "<conn> enter stop to quit timelapse:"); fflush(stdout);
	
			FD_ZERO(&rfds);
			FD_SET(pipes[0], &rfds);
			FD_SET(fileno(stdin), &rfds);
			ret = select(getdtablesize() + 1, &rfds, NULL, NULL, NULL);
			switch (ret) {
			case -1:
				perror("select");
				exit(EXIT_FAILURE);
			case 0:
				fprintf(stderr, "select time out, shouldn't happen at all");
				continue;
			default:
				if (FD_ISSET(fileno(stdin), &rfds)) {
					if ( fgets(buf,CHDKBUFS-1,stdin) == NULL )
					{
						printf("\n");
						break;
					}
					s = buf+strlen(buf)-1;
					while ( s >= buf && ( *s == '\n' || *s == '\r' ) )
					{
						*s = '\0';
						s--;
					}
	
					if (!strcasecmp("stop",buf))
					{
						kill(pid, SIGKILL);
						close(pipes[0]);
						wait(NULL);
						goto ret;
					}
				}
				if (FD_ISSET(pipes[0], &rfds)) {
					ret = read(pipes[0], &c, 1);
					if (ret != 1 || c == 'c') {
						fprintf(stdout, "\nHardware limit reached, will stop...\n");
						fprintf(stdout, "To take more pictures or time lapses again, please enter commands\n");
						kill(pid, SIGKILL);
						close(pipes[0]);
						wait(NULL);
						goto ret;
					} 		
				}
			}
		}
ret:
		sleep(5);
		//siglongjmp(env, pid);
		//break;
		return;
	}	

}

/*******************************************************************************
* Function     : get_all_files                                                 *
* Parameters   : overwrite -- whether or not to overwrite existing image which *
*                are already downloaded                                        *
* Return value : n/a                                                           *
* Functionality:                                                               *
*   Download all the image files from the camera into the DCIM directory in the*
*   project directory                                                          *
*******************************************************************************/
void
get_all_files (int busn, int devn, short force, int overwrite)
{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
	int i;

//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
	//printf("Camera: %s\n",params.deviceinfo.Model);

	//get the handles of all the objects which resides in the camera
	CR(ptp_getobjecthandles (&params,0xffffffff, 0x000000, 0x000000,
		&params.handles),"Could not get object handles\n");

	//for aeach object, call the get_save_object function to save it
	//if it is a image
	for (i=0; i<params.handles.n; i++) {
	    get_save_object (&params, params.handles.Handler[i], NULL,
		    overwrite);
	}
//	close_camera(&ptp_usb, &params, dev);

	//output message to indicate successful download
	printf("Download completed.\n");

	//reset the connection so that the camera doesn't shutdown
	reset_connection();
}

//void
//list_operations (int busn, int devn, short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	int i;
//	const char* name;
//
//	printf("\nListing supported operations...\n");
//
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//	printf("Camera: %s\n",params.deviceinfo.Model);
//	for (i=0; i<params.deviceinfo.OperationsSupported_len; i++)
//	{
//		name=ptp_get_operation_name(&params,
//			params.deviceinfo.OperationsSupported[i]);
//
//		if (name==NULL)
//			printf("  0x%04x: UNKNOWN\n",
//				params.deviceinfo.OperationsSupported[i]);
//		else
//			printf("  0x%04x: %s\n",
//				params.deviceinfo.OperationsSupported[i],name);
//	}
//	close_camera(&ptp_usb, &params, dev);
//
//}

//void
//list_properties (int busn, int devn, short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	const char* propname;
//	int i;
//
//	printf("\nListing properties...\n");
//
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
///* XXX */
//#if 0
//	CR(ptp_nikon_setcontrolmode(&params, 0x01),
//		"Unable to set Nikon PC controll mode\n");
//#endif
//	printf("Camera: %s\n",params.deviceinfo.Model);
//	for (i=0; i<params.deviceinfo.DevicePropertiesSupported_len;i++){
//		propname=ptp_prop_getname(&params,
//			params.deviceinfo.DevicePropertiesSupported[i]);
//		if (propname!=NULL) 
//			printf("  0x%04x: %s\n",
//				params.deviceinfo.DevicePropertiesSupported[i],
//				propname);
//		else
//			printf("  0x%04x: UNKNOWN\n",
//				params.deviceinfo.DevicePropertiesSupported[i]);
//	}
//	close_camera(&ptp_usb, &params, dev);
//}

short
print_propval (uint16_t datatype, void* value, short hex);
short
print_propval (uint16_t datatype, void* value, short hex)
{
	switch (datatype) {
		case PTP_DTC_INT8:
			printf("%hhi",*(char*)value);
			return 0;
		case PTP_DTC_UINT8:
			printf("%hhu",*(unsigned char*)value);
			return 0;
		case PTP_DTC_INT16:
			printf("%hi",*(int16_t*)value);
			return 0;
		case PTP_DTC_UINT16:
			if (hex==PTPCAM_PRINT_HEX)
				printf("0x%04hX (%hi)",*(uint16_t*)value,
					*(uint16_t*)value);
			else
				printf("%hi",*(uint16_t*)value);
			return 0;
		case PTP_DTC_INT32:
			printf("%li",(long int)*(int32_t*)value);
			return 0;
		case PTP_DTC_UINT32:
			if (hex==PTPCAM_PRINT_HEX)
				printf("0x%08lX (%lu)",
					(long unsigned) *(uint32_t*)value,
					(long unsigned) *(uint32_t*)value);
			else
				printf("%lu",(long unsigned)*(uint32_t*)value);
			return 0;
		case PTP_DTC_STR:
			printf("\"%s\"",(char *)value);
	}
	return -1;
}

uint16_t
set_property (PTPParams* params,
		uint16_t property, const char* value, uint16_t datatype);
uint16_t
set_property (PTPParams* params,
		uint16_t property, const char* value, uint16_t datatype)
{
	void* val=NULL;

	switch(datatype) {
	case PTP_DTC_INT8:
		val=malloc(sizeof(int8_t));
		*(int8_t*)val=(int8_t)strtol(value,NULL,0);
		break;
	case PTP_DTC_UINT8:
		val=malloc(sizeof(uint8_t));
		*(uint8_t*)val=(uint8_t)strtol(value,NULL,0);
		break;
	case PTP_DTC_INT16:
		val=malloc(sizeof(int16_t));
		*(int16_t*)val=(int16_t)strtol(value,NULL,0);
		break;
	case PTP_DTC_UINT16:
		val=malloc(sizeof(uint16_t));
		*(uint16_t*)val=(uint16_t)strtol(value,NULL,0);
		break;
	case PTP_DTC_INT32:
		val=malloc(sizeof(int32_t));
		*(int32_t*)val=(int32_t)strtol(value,NULL,0);
		break;
	case PTP_DTC_UINT32:
		val=malloc(sizeof(uint32_t));
		*(uint32_t*)val=(uint32_t)strtol(value,NULL,0);
		break;
	case PTP_DTC_STR:
		val=(void *)value;
	}
	return(ptp_setdevicepropvalue(params, property, val, datatype));
	free(val);
	return 0;
}
void
getset_property_internal (PTPParams* params, uint16_t property,const char* value);
void
getset_property_internal (PTPParams* params, uint16_t property,const char* value)
{
	PTPDevicePropDesc dpd;
	const char* propname;
	const char *propdesc;
	uint16_t result;


	memset(&dpd,0,sizeof(dpd));
	result=ptp_getdevicepropdesc(params,property,&dpd);
	if (result!=PTP_RC_OK) {
		ptp_perror(params,result); 
		fprintf(stderr,"ERROR: "
		"Could not get device property description!\n"
		"Try to reset the camera.\n");
		return ;
	}
	/* until this point dpd has to be free()ed */
	propdesc=ptp_prop_getdesc(params, &dpd, NULL);
	propname=ptp_prop_getname(params,property);

	if (value==NULL) { /* property GET */
		if (!verbose) { /* short output, default */
			printf("'%s' is set to: ", propname==NULL?"UNKNOWN":propname);
			if (propdesc!=NULL)
				printf("[%s]", propdesc);
			else 
			{
				if (dpd.FormFlag==PTP_DPFF_Enumeration)
					PRINT_PROPVAL_HEX(dpd.CurrentValue);
				else 
					PRINT_PROPVAL_DEC(dpd.CurrentValue);
			}
			printf("\n");
	
		} else { /* verbose output */
	
			printf("%s: [0x%04x, ",propname==NULL?"UNKNOWN":propname,
					property);
			if (dpd.GetSet==PTP_DPGS_Get)
				printf ("readonly, ");
			else
				printf ("readwrite, ");
			printf ("%s] ",
				ptp_get_datatype_name(params, dpd.DataType));

			printf ("\n  Current value: ");
			if (dpd.FormFlag==PTP_DPFF_Enumeration)
				PRINT_PROPVAL_HEX(dpd.CurrentValue);
			else 
				PRINT_PROPVAL_DEC(dpd.CurrentValue);

			if (propdesc!=NULL)
				printf(" [%s]", propdesc);
			printf ("\n  Factory value: ");
			if (dpd.FormFlag==PTP_DPFF_Enumeration)
				PRINT_PROPVAL_HEX(dpd.FactoryDefaultValue);
			else 
				PRINT_PROPVAL_DEC(dpd.FactoryDefaultValue);
			propdesc=ptp_prop_getdesc(params, &dpd,
						dpd.FactoryDefaultValue);
			if (propdesc!=NULL)
				printf(" [%s]", propdesc);
			printf("\n");

			switch (dpd.FormFlag) {
			case PTP_DPFF_Enumeration:
				{
					int i;
					printf ("Enumerated:\n");
					for(i=0;i<dpd.FORM.Enum.NumberOfValues;i++){
						PRINT_PROPVAL_HEX(
						dpd.FORM.Enum.SupportedValue[i]);
						propdesc=ptp_prop_getdesc(params, &dpd, dpd.FORM.Enum.SupportedValue[i]);
						if (propdesc!=NULL) printf("\t[%s]", propdesc);
						printf("\n");
					}
				}
				break;
			case PTP_DPFF_Range:
				printf ("Range [");
				PRINT_PROPVAL_DEC(dpd.FORM.Range.MinimumValue);
				printf(" - ");
				PRINT_PROPVAL_DEC(dpd.FORM.Range.MaximumValue);
				printf("; step ");
				PRINT_PROPVAL_DEC(dpd.FORM.Range.StepSize);
				printf("]\n");
				break;
			case PTP_DPFF_None:
				break;
			}
		}
	} else {
		uint16_t r;
		propdesc=ptp_prop_getdesc(params, &dpd, NULL);
		printf("'%s' is set to: ", propname==NULL?"UNKNOWN":propname);
		if (propdesc!=NULL)
			printf("[%s]", propdesc);
		else
		{
			if (dpd.FormFlag==PTP_DPFF_Enumeration)
				PRINT_PROPVAL_HEX(dpd.CurrentValue);
			else 
				PRINT_PROPVAL_DEC(dpd.CurrentValue);
		}
		printf("\n");

		propdesc=ptp_prop_getdescbystring(params, &dpd, value);
/*
		if (propdesc==NULL) {
			fprintf(stderr, "ERROR: Unable to set property to unidentified value: '%s'\n",
				value);
			goto out;
		}
*/
		printf("Changing property value to %s [%s] ",
			value,propdesc);
		r=(set_property(params, property, value, dpd.DataType));
		if (r!=PTP_RC_OK)
		{
			printf ("FAILED!!!\n");
			fflush(NULL);
		        ptp_perror(params,r);
		}
		else 
			printf ("succeeded.\n");
	}
/*	out: */

	ptp_free_devicepropdesc(&dpd);
}

//void
//getset_propertybyname (int busn,int devn,char* property,char* value,short force);
//void
//getset_propertybyname (int busn,int devn,char* property,char* value,short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	char *p;
//	uint16_t dpc;
//	const char *propval=NULL;
//
//	printf("\n");
//
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	printf("Camera: %s",params.deviceinfo.Model);
//	if ((devn!=0)||(busn!=0)) 
//		printf(" (bus %i, dev %i)\n",busn,devn);
//	else
//		printf("\n");
//
//	if (property==NULL) {
//		fprintf(stderr,"ERROR: no such property\n");
//		return;
//	}
//		
//	/* 
//	1. change all '-' in property and value to ' '
//	2. change all '  ' in property and value to '-'
//	3. get property code by name
//	4. get value code by name
//	5. set property
//	*/
//	while ((p=strchr(property,'-'))!=NULL) {
//		*p=' ';
//	}
//
//	dpc=ptp_prop_getcodebyname(&params, property);
//	if (dpc==0) {
//		fprintf(stderr, "ERROR: Could not find property '%s'\n",
//			property);
//		close_camera(&ptp_usb, &params, dev);
//		return;
//	}
//
//	if (!ptp_property_issupported(&params, dpc))
//	{
//		fprintf(stderr,"The device does not support this property!\n");
//		close_camera(&ptp_usb, &params, dev);
//		return;
//	}
//
//	if (value!=NULL) {
//		while ((p=strchr(value,'-'))!=NULL) {
//			*p=' ';
//		}
//		propval=ptp_prop_getvalbyname(&params, value, dpc);
//		if (propval==NULL) propval=value;
//	}
//
//	getset_property_internal (&params, dpc,propval);
//
//	close_camera(&ptp_usb, &params, dev);
//}
//void
//getset_property (int busn,int devn,uint16_t property,char* value,short force);
//void
//getset_property (int busn,int devn,uint16_t property,char* value,short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//
//	printf ("\n");
//
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	printf("Camera: %s",params.deviceinfo.Model);
//	if ((devn!=0)||(busn!=0)) 
//		printf(" (bus %i, dev %i)\n",busn,devn);
//	else
//		printf("\n");
//	if (!ptp_property_issupported(&params, property))
//	{
//		fprintf(stderr,"The device does not support this property!\n");
//		close_camera(&ptp_usb, &params, dev);
//		return;
//	}
//
//	getset_property_internal (&params, property,value);
//#if 0
//	memset(&dpd,0,sizeof(dpd));
//	CR(ptp_getdevicepropdesc(&params,property,&dpd),
//		"Could not get device property description!\n"
//		"Try to reset the camera.\n");
//	propdesc= ptp_prop_getdesc(&params, &dpd, NULL);
//	propname=ptp_prop_getname(&params,property);
//
//	if (value==NULL) { /* property GET */
//		if (!verbose) { /* short output, default */
//			printf("'%s' is set to: ", propname==NULL?"UNKNOWN":propname);
//			if (propdesc!=NULL)
//				printf("%s [%s]", ptp_prop_tostr(&params, &dpd,
//							NULL), propdesc);
//			else 
//			{
//				if (dpd.FormFlag==PTP_DPFF_Enumeration)
//					PRINT_PROPVAL_HEX(dpd.CurrentValue);
//				else 
//					PRINT_PROPVAL_DEC(dpd.CurrentValue);
//			}
//			printf("\n");
//	
//		} else { /* verbose output */
//	
//			printf("%s: [0x%04x, ",propname==NULL?"UNKNOWN":propname,
//					property);
//			if (dpd.GetSet==PTP_DPGS_Get)
//				printf ("readonly, ");
//			else
//				printf ("readwrite, ");
//			printf ("%s] ",
//				ptp_get_datatype_name(&params, dpd.DataType));
//
//			printf ("\n  Current value: ");
//			if (dpd.FormFlag==PTP_DPFF_Enumeration)
//				PRINT_PROPVAL_HEX(dpd.CurrentValue);
//			else 
//				PRINT_PROPVAL_DEC(dpd.CurrentValue);
//
//			if (propdesc!=NULL)
//				printf(" [%s]", propdesc);
//			printf ("\n  Factory value: ");
//			if (dpd.FormFlag==PTP_DPFF_Enumeration)
//				PRINT_PROPVAL_HEX(dpd.FactoryDefaultValue);
//			else 
//				PRINT_PROPVAL_DEC(dpd.FactoryDefaultValue);
//			propdesc=ptp_prop_getdesc(&params, &dpd,
//						dpd.FactoryDefaultValue);
//			if (propdesc!=NULL)
//				printf(" [%s]", propdesc);
//			printf("\n");
//
//			switch (dpd.FormFlag) {
//			case PTP_DPFF_Enumeration:
//				{
//					int i;
//					printf ("Enumerated:\n");
//					for(i=0;i<dpd.FORM.Enum.NumberOfValues;i++){
//						PRINT_PROPVAL_HEX(
//						dpd.FORM.Enum.SupportedValue[i]);
//						propdesc=ptp_prop_getdesc(&params, &dpd, dpd.FORM.Enum.SupportedValue[i]);
//						if (propdesc!=NULL) printf("\t[%s]", propdesc);
//						printf("\n");
//					}
//				}
//				break;
//			case PTP_DPFF_Range:
//				printf ("Range [");
//				PRINT_PROPVAL_DEC(dpd.FORM.Range.MinimumValue);
//				printf(" - ");
//				PRINT_PROPVAL_DEC(dpd.FORM.Range.MaximumValue);
//				printf("; step ");
//				PRINT_PROPVAL_DEC(dpd.FORM.Range.StepSize);
//				printf("]\n");
//				break;
//			case PTP_DPFF_None:
//				break;
//			}
//		}
//	} else {
//		uint16_t r;
//		propdesc= ptp_prop_getdesc(&params, &dpd, NULL);
//		printf("'%s' is set to: ", propname==NULL?"UNKNOWN":propname);
//		if (propdesc!=NULL)
//			printf("%s [%s]", ptp_prop_tostr(&params, &dpd, NULL), propdesc);
//		else
//		{
//			if (dpd.FormFlag==PTP_DPFF_Enumeration)
//				PRINT_PROPVAL_HEX(dpd.CurrentValue);
//			else 
//				PRINT_PROPVAL_DEC(dpd.CurrentValue);
//		}
//		printf("\n");
//		printf("Changing property value to '%s' ",value);
//		r=(set_property(&params, property, value, dpd.DataType));
//		if (r!=PTP_RC_OK)
//		{
//			printf ("FAILED!!!\n");
//			fflush(NULL);
//		        ptp_perror(&params,r);
//		}
//		else 
//			printf ("succeeded.\n");
//	}
//	ptp_free_devicepropdesc(&dpd);
//#endif
//
//	close_camera(&ptp_usb, &params, dev);
//}

//void
//show_all_properties (int busn,int devn,short force, int unknown);
//void
//show_all_properties (int busn,int devn,short force, int unknown)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	PTPDevicePropDesc dpd;
//	const char* propname;
//	const char *propdesc;
//	int i;
//
//	printf ("\n");
//
//	if (open_camera(busn, devn, force, &ptp_usb, &params, &dev)<0)
//		return;
//
//	printf("Camera: %s",params.deviceinfo.Model);
//	if ((devn!=0)||(busn!=0)) 
//		printf(" (bus %i, dev %i)\n",busn,devn);
//	else
//		printf("\n");
//
//	for (i=0; i<params.deviceinfo.DevicePropertiesSupported_len;i++) {
//		propname=ptp_prop_getname(&params,
//				params.deviceinfo.DevicePropertiesSupported[i]);
//		if ((unknown) && (propname!=NULL)) continue;
//
//		printf("0x%04x: ",
//				params.deviceinfo.DevicePropertiesSupported[i]);
//		memset(&dpd,0,sizeof(dpd));
//		CR(ptp_getdevicepropdesc(&params,
//			params.deviceinfo.DevicePropertiesSupported[i],&dpd),
//			"Could not get device property description!\n"
//			"Try to reset the camera.\n");
//		propdesc= ptp_prop_getdesc(&params, &dpd, NULL);
//
//		PRINT_PROPVAL_HEX(dpd.CurrentValue);
//		if (verbose) {
//			printf (" (%s",propname==NULL?"UNKNOWN":propname);
//			if (propdesc!=NULL)
//				printf(": %s)",propdesc);
//			else
//				printf(")");
//		}
//	
//		printf("\n");
//		ptp_free_devicepropdesc(&dpd);
//	}
//
//	close_camera(&ptp_usb, &params, dev);
//}

int
usb_get_endpoint_status(PTP_USB* ptp_usb, int ep, uint16_t* status)
{
	 return (usb_control_msg(ptp_usb->handle,
		USB_DP_DTH|USB_RECIP_ENDPOINT, USB_REQ_GET_STATUS,
		USB_FEATURE_HALT, ep, (char *)status, 2, 3000));
}

int
usb_clear_stall_feature(PTP_USB* ptp_usb, int ep)
{

	return (usb_control_msg(ptp_usb->handle,
		USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE, USB_FEATURE_HALT,
		ep, NULL, 0, 3000));
}

int
usb_ptp_get_device_status(PTP_USB* ptp_usb, uint16_t* devstatus);
int
usb_ptp_get_device_status(PTP_USB* ptp_usb, uint16_t* devstatus)
{
	return (usb_control_msg(ptp_usb->handle,
		USB_DP_DTH|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
		USB_REQ_GET_DEVICE_STATUS, 0, 0,
		(char *)devstatus, 4, 3000));
}

int
usb_ptp_device_reset(PTP_USB* ptp_usb);
int
usb_ptp_device_reset(PTP_USB* ptp_usb)
{
	return (usb_control_msg(ptp_usb->handle,
		USB_TYPE_CLASS|USB_RECIP_INTERFACE,
		USB_REQ_DEVICE_RESET, 0, 0, NULL, 0, 3000));
}

//void
//reset_device (int busn, int devn, short force);
//void
//reset_device (int busn, int devn, short force)
//{
//	PTPParams params;
//	PTP_USB ptp_usb;
//	struct usb_device *dev;
//	uint16_t status;
//	uint16_t devstatus[2] = {0,0};
//	int ret;
//
//#ifdef DEBUG
//	printf("dev %i\tbus %i\n",devn,busn);
//#endif
//	dev=find_device(busn,devn,force);
//	if (dev==NULL) {
//		fprintf(stderr,"could not find any device matching given "
//		"bus/dev numbers\n");
//		close(gpsfd);
//		exit(-1);
//	}
//	find_endpoints(dev,&ptp_usb.inep,&ptp_usb.outep,&ptp_usb.intep);
//
//	init_ptp_usb(&params, &ptp_usb, dev);
//	
//	/* get device status (devices likes that regardless of its result)*/
//	usb_ptp_get_device_status(&ptp_usb,devstatus);
//	
//	/* check the in endpoint status*/
//	ret = usb_get_endpoint_status(&ptp_usb,ptp_usb.inep,&status);
//	if (ret<0) perror ("usb_get_endpoint_status()");
//	/* and clear the HALT condition if happend*/
//	if (status) {
//		printf("Resetting input pipe!\n");
//		ret=usb_clear_stall_feature(&ptp_usb,ptp_usb.inep);
//		if (ret<0)perror ("usb_clear_stall_feature()");
//	}
//	status=0;
//	/* check the out endpoint status*/
//	ret = usb_get_endpoint_status(&ptp_usb,ptp_usb.outep,&status);
//	if (ret<0) perror ("usb_get_endpoint_status()");
//	/* and clear the HALT condition if happend*/
//	if (status) {
//		printf("Resetting output pipe!\n");
//		ret=usb_clear_stall_feature(&ptp_usb,ptp_usb.outep);
//		if (ret<0)perror ("usb_clear_stall_feature()");
//	}
//	status=0;
//	/* check the interrupt endpoint status*/
//	ret = usb_get_endpoint_status(&ptp_usb,ptp_usb.intep,&status);
//	if (ret<0)perror ("usb_get_endpoint_status()");
//	/* and clear the HALT condition if happend*/
//	if (status) {
//		printf ("Resetting interrupt pipe!\n");
//		ret=usb_clear_stall_feature(&ptp_usb,ptp_usb.intep);
//		if (ret<0)perror ("usb_clear_stall_feature()");
//	}
//
//	/* get device status (now there should be some results)*/
//	ret = usb_ptp_get_device_status(&ptp_usb,devstatus);
//	if (ret<0) 
//		perror ("usb_ptp_get_device_status()");
//	else	{
//		if (devstatus[1]==PTP_RC_OK) 
//			printf ("Device status OK\n");
//		else
//			printf ("Device status 0x%04x\n",devstatus[1]);
//	}
//	
//	/* finally reset the device (that clears prevoiusly opened sessions)*/
//	ret = usb_ptp_device_reset(&ptp_usb);
//	if (ret<0)perror ("usb_ptp_device_reset()");
//	/* get device status (devices likes that regardless of its result)*/
//	usb_ptp_get_device_status(&ptp_usb,devstatus);
//
//	close_usb(&ptp_usb, dev);
//
//}

/* main program  */

int chdk(int busn, int devn, short force);
int start_server();
uint8_t chdkmode=0;
char chdkarg[CHDKBUFS];
long port;

/*******************************************************************************
* Name         : main                                                          *
* Parameters   : argc -- the number of command line arguments                  *
*                argv -- the array of command line arguments                   *
* Functionality: Starting point of the program.                                *
*******************************************************************************/
int
main(int argc, char ** argv)
{
	int busn=0,devn=0;
	int action=0;
	short force=0;
	//int overwrite=SKIP_IF_EXISTS;
	//uint16_t property=0;
	//char* value=NULL;
	//char* propstr=NULL;
	//uint32_t handle=0;
	//char *filename=NULL;
	//int num=0;
	/* parse options */
	int option_index = 0,opt;
	static struct option loptions[] = {
		{"help",0,0,'h'},
		{"bus",1,0,0},
		{"dev",1,0,0},
		{"reset",0,0,'r'},
		{"list-devices",0,0,'l'},
		{"list-files",0,0,'L'},
		{"list-operations",1,0,'o'},
		{"list-properties",0,0,'p'},
		{"show-all-properties",0,0,0},
		{"show-unknown-properties",0,0,0},
		{"show-property",1,0,'s'},
		{"set-property",1,0,'s'},
		{"set",1,0,0},
		{"get-file",1,0,'g'},
		{"get-all-files",0,0,'G'},
		{"capture",0,0,'c'},
		{"nikon-dc",0,0,0},
		{"ndc",0,0,0},
		{"nikon-ic",0,0,0},
		{"nic",0,0,0},
		{"nikon-dc2",0,0,0},
		{"ndc2",0,0,0},
		{"loop-capture",1,0,0},
		{"delete-object",1,0,'d'},
		{"delete-all-files",1,0,'D'},
		{"info",0,0,'i'},
		{"val",1,0,0},
		{"filename",1,0,0},
		{"overwrite",0,0,0},
		{"force",0,0,'f'},
		{"verbose",2,0,'v'},
		{"chdk",2,0,0},
		/* Added by Haoyuan */
		{"server" ,0,0,0},
		{0,0,0,0}
	};

	/* register signal handlers */
	signal(SIGINT, ptpcam_siginthandler);
	
	while(1) {
		opt = getopt_long (argc, argv, "LhlcipfroGg:Dd:s:v::", loptions, &option_index);
		if (opt==-1) break;
	
		switch (opt) {
		/* set parameters */
		case 0:
			if (!(strcmp("chdk",loptions[option_index].name)))
			{
				action=ACT_CHDK;
				if (optarg) {
					chdkmode=CHDK_MODE_CLI;
					strncpy(chdkarg, optarg, CHDKBUFS-1);
					chdkarg[CHDKBUFS-1]='\0';
				} else {
					chdkmode=CHDK_MODE_INTERACTIVE;
				}
			}
			if (!(strcmp("server",loptions[option_index].name)))
			{
				action=ACT_SERVER;
			}
/*
			if (!(strcmp("val",loptions[option_index].name)))
				value=strdup(optarg);
			if (!(strcmp("filename",loptions[option_index].name)))
				filename=strdup(optarg);
			if (!(strcmp("overwrite",loptions[option_index].name)))
				overwrite=OVERWRITE_EXISTING;
			if (!(strcmp("bus",loptions[option_index].name)))
				busn=strtol(optarg,NULL,10);
			if (!(strcmp("dev",loptions[option_index].name)))
				devn=strtol(optarg,NULL,10);
			if (!(strcmp("loop-capture",loptions[option_index].name)))
			{
				action=ACT_LOOP_CAPTURE;
				num=strtol(optarg,NULL,10);
			}
			if (!(strcmp("show-all-properties", loptions[option_index].name)))
				action=ACT_SHOW_ALL_PROPERTIES;
			if (!(strcmp("show-unknown-properties", loptions[option_index].name)))
				action=ACT_SHOW_UNKNOWN_PROPERTIES;
			if (!(strcmp("set",loptions[option_index].name)))
			{
				propstr=strdup(optarg);
				action=ACT_SET_PROPBYNAME;
			}
			if (!strcmp("nikon-dc", loptions[option_index].name) ||
			    !strcmp("ndc", loptions[option_index].name))
			{
				action=ACT_NIKON_DC;
			}
			if (!strcmp("nikon-ic", loptions[option_index].name) ||
			    !strcmp("nic", loptions[option_index].name))
			{
			    action=ACT_NIKON_IC;
			}
			if (!strcmp("nikon-dc2", loptions[option_index].name) ||
			    !strcmp("ndc2", loptions[option_index].name))
			{
			    action=ACT_NIKON_DC2;
			}
*/
			break;
/*
		case 'f':
			force=~force;
			break;
		case 'v':
			if (optarg) 
				verbose=strtol(optarg,NULL,10);
			else
				verbose=1;
*/
			/*printf("VERBOSE LEVEL  = %i\n",verbose);*/
			//break;
		/* actions */
/*
		case 'h':
			help();
			break;
		case 'r':
			action=ACT_DEVICE_RESET;
			break;
		case 'l':
			action=ACT_LIST_DEVICES;
			break;
		case 'p':
			action=ACT_LIST_PROPERTIES;
			break;
		case 's':
			action=ACT_GETSET_PROPERTY;
			property=strtol(optarg,NULL,16);
			break;
		case 'o':
			action=ACT_LIST_OPERATIONS;
			break;
		case 'i':
			action=ACT_SHOW_INFO;
			break;
		case 'c':
			action=ACT_CAPTURE;
			break;
		case 'L':
			action=ACT_LIST_FILES;
			break;
		case 'g':
			action=ACT_GET_FILE;
			handle=strtol(optarg,NULL,16);
			break;
		case 'G':
			action=ACT_GET_ALL_FILES;
			break;
		case 'd':
			action=ACT_DELETE_OBJECT;
			handle=strtol(optarg,NULL,16);
			break;
		case 'D':
			action=ACT_DELETE_ALL_FILES;
		case '?':
			break;
*/
		default:
			fprintf(stderr,"getopt returned character code 0%o\n",
				opt);
			break;
		}
	}
	if (argc==1) {
		usage();
		return 0;
	}
	/* open file for storing GPS information */
	gpsfd = open(GPSFILE, O_CREAT | O_APPEND | O_RDWR, 0644);
	if (gpsfd < 0) {
		perror("Cannot open GPS file:");
		exit(EXIT_FAILURE);
	}
	
	switch (action) {
                case ACT_CHDK:
                        chdk(busn,devn,force);
                        break;
				//Added by Haoyuan
		case ACT_SERVER:
						start_server();
						chdk(busn, devn, force);
						break;
/*
		case ACT_DEVICE_RESET:
			reset_device(busn,devn,force);
			break;
		case ACT_LIST_DEVICES:
			list_devices(force);
			break;
		case ACT_LIST_PROPERTIES:
			list_properties(busn,devn,force);
			break;
		case ACT_GETSET_PROPERTY:
			getset_property(busn,devn,property,value,force);
			break;
		case ACT_SHOW_INFO:
			show_info(busn,devn,force);
			break;
		case ACT_LIST_OPERATIONS:
			list_operations(busn,devn,force);
			break;
		case ACT_LIST_FILES:
			list_files(busn,devn,force);
			break;
		case ACT_GET_FILE:
			get_file(busn,devn,force,handle,filename,overwrite);
			break;
		case ACT_GET_ALL_FILES:
			get_all_files(busn,devn,force,overwrite);
			break;
		case ACT_CAPTURE:
			capture_image(busn,devn,force);
			break;
		case ACT_DELETE_OBJECT:
			delete_object(busn,devn,force,handle);
			break;
		case ACT_DELETE_ALL_FILES:
			delete_all_files(busn,devn,force);
			break;
		case ACT_LOOP_CAPTURE:
			loop_capture (busn,devn,force,num,overwrite);
			break;
		case ACT_SHOW_ALL_PROPERTIES:
			show_all_properties(busn,devn,force,0);
			break;
		case ACT_SHOW_UNKNOWN_PROPERTIES:
			show_all_properties(busn,devn,force,1);
			break;
		case ACT_SET_PROPBYNAME:
			getset_propertybyname(busn,devn,propstr,value,force);
			break;
		case ACT_NIKON_DC:
			nikon_direct_capture(busn,devn,force,filename,overwrite);
			break;
		case ACT_NIKON_IC:
			nikon_initiate_dc (busn,devn,force);
			break;
		case ACT_NIKON_DC2:
			nikon_direct_capture2(busn,devn,force,filename,overwrite);
*/
	}

	return 0;

}


static void open_connection()
{
  connected = (0 == open_camera(camera_bus,camera_dev,camera_force,&ptp_usb,&params,&dev));
  if ( connected )
  {
    int major,minor;
    if ( !ptp_chdk_get_version(&params,&params.deviceinfo,&major,&minor) )
    {
      printf("error: cannot get camera CHDK PTP version; either it has an unsupported version or no CHDK PTP support at all\n");
    } else if ( major != PTP_CHDK_VERSION_MAJOR || minor < PTP_CHDK_VERSION_MINOR )
    {
      printf("error: camera has unsupported camera version %i.%i; some functionality may be missing or cause unintented consequences\n",major,minor);
    }
  }
}

static void close_connection()
{
  close_camera(&ptp_usb,&params,dev);
}

static void reset_connection()
{
  if ( connected )
  {
    close_connection();
  }
  open_connection();
}

static void print_safe(char *buf, int size)
{
  int i;
  for (i=0; i<size; i++)
  {
    if ( buf[i] < ' ' || buf[i] > '~' )
    {
      printf(".");
    } else {
      printf("%c",buf[i]);
    }
  }
}

static void hexdump(char *buf, unsigned int size, unsigned int offset)
{
  unsigned int i;
  char s[16];

  if ( offset % 16 != 0 )
  {
      printf("%08x  ",offset);
      for (i=0; i<(offset%16); i++)
      {
        printf("   ");
      }
      if ( offset % 16 > 8 )
      {
        printf(" ");
      }
      memset(s,' ',offset%16);
  }
  for (i=0; ; i++, offset++)
  {
    if ( offset % 16 == 0 )
    {
      if ( i > 0 )
      {
        printf(" |");
        print_safe(s,16);
        printf("|\n");
      }
      printf("%08x",offset);
      if (i < size)
      {
        printf(" ");
      }
    }
    if ( offset % 8 == 0 )
    {
      printf(" ");
    }
    if ( i == size )
    {
      break;
    }
    printf("%02x ",(unsigned char) buf[i]);
    s[offset%16] = buf[i];
  }
  if ( offset % 16 != 0 )
  {
      for (i=0; i<16-(offset%16); i++)
      {
        printf("   ");
      }
      if ( offset % 16 < 8 )
      {
        printf(" ");
      }
      memset(s+(offset%16),' ',16-(offset%16));
      printf(" |");
      print_safe(s,16);
      printf("|\n%08x",offset);
  }
  printf("\n");
}

int start_server() 
{
	int serverfd;
	struct sockaddr_in serveraddr;
	
	serverfd = socket(AF_INET, SOCK_STREAM, 0);
	if (serverfd < 0) {
		perror("socket");
		close(gpsfd);
		exit(EXIT_FAILURE);
	}

	memset(&serveraddr, 0, sizeof(struct sockaddr_in));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = INADDR_ANY;
	serveraddr.sin_port = htons(PORT);

	if (bind(serverfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr))) {
		perror("bind");
		close(serverfd);
		close(gpsfd);
		exit(EXIT_FAILURE);
	}

	if (listen(serverfd, 6)) {
		perror("listen");
		close(serverfd);
		close(gpsfd);
		exit(EXIT_FAILURE);
	}

	clientfd = accept(serverfd, NULL, NULL);		
	if (clientfd < 0) {
		perror("accept");
		close(serverfd);
		close(gpsfd);
		exit(EXIT_FAILURE);
	}

	close(0); close(1); close(2);
	dup2(clientfd, 0);
	dup2(clientfd, 1);
	dup2(clientfd, 2);
	//dup2(clientfd, fileno(stdin));
	//dup2(clientfd, fileno(stdout));
	//dup2(clientfd, fileno(stderr));
	
	return 0;
}

/*******************************************************************************
* Name         : chdk                                                          *
* Parameters   : busn -- the usb bus number                                    *
*                devn -- the usb device number                                 *
*                force -- force write or not                                   *
* Functionality: The real starting point of the program. Initialize the usb    *
*   connection, output a prompt, wait for user input in a loop, and take on    *
*   action base on user input, actions include taking a picture, downloading   *
*   pictures, taking picture with time lapse, adding GPS information, changing *
*   camera settings, and display help information                              *
*******************************************************************************/
int chdk(int busn, int devn, short force)
{
  char buf[CHDKBUFS], *s;
  int i;

  camera_bus = busn;
  camera_dev = devn;
  camera_force = force;
  
  memset(buf, 0, CHDKBUFS * sizeof(char));
  open_connection();

  if (chdkmode==CHDK_MODE_CLI) {
    // printf("CHDK command line mode. Argument:%s\n", chdkarg);
    if (!connected) {
      printf("error: couldn't connect to camera.\n");
      return(-1);
    	}
}
	//on boot default to mode 1, if not, the camera cannot take picture after download
	if ( !ptp_chdk_switch_mode(strtoul("1",NULL,0),&params,&params.deviceinfo) )
	{
	printf("mode switch failed!\n");
	}

	//sigsetjmp(env, 1);

  while (1) {
    if (chdkmode==CHDK_MODE_CLI) {
		fprintf(stdout, "CHDK_MODE_CLI\n"); fflush(stdout);
      for (i=0; i<CHDKBUFS; i++) {
        buf[i]=chdkarg[i];
      }
    } else { // CHDK_MODE_INTERACTIVE
      printf("%s ", connected ? "<conn>" : "<    >"); fflush(NULL);
      if ( fgets(buf,CHDKBUFS-1,stdin) == NULL )
      {
        printf("\n");
        break;
      }
    }
    s = buf+strlen(buf)-1;
    while ( s >= buf && ( *s == '\n' || *s == '\r' ) )
    {
      *s = '\0';
      s--;
    }

    if ( !strcmp("q",buf) || !strcmp("quit",buf) )
    {
      break;
      
    } else if ( !strcmp("h",buf) || !strcmp("help",buf) )
    {
      printf(
          "  version                      get CHDK PTP version (ptpcam and camera)\n"
          "h help                         list commands\n"
          "  change_mode <mode_name>      change the current shoot mode for the camera.\n"
          "                               (mode_name: 'M', 'AV', 'TV', 'P')\n"
          "  mode <val>                   set mode (0=playback,1=record)\n"
          "c capture                      shoot single picture\n"
          "s shutter <val>                set shutter speed (1/4-1/2000s)\n"
          "i iso <val>                    set iso speed\n"
          "                               (can only be set as 80,100,200,400,800)\n"
          "a aperture  <vaL>              set aperture (2.8-8.0)\n"
          "z zoom <val>                   zoom (factor between -15 to 15)\n"
	  "t timelapse  <val>             taking pictures with specified time lapse\n"
          //"  autof <val>                  toggle autofocus (0=manual,1=auto)\n"
 	  "  download                     download all files in the camera\n" 
	  "  list                         list all the files in the camera\n"
          "q quit                         quit program\n"
          "r reset                        reconnect to camera\n"
//          "  shutdown-hard                shutdown camera (hard)\n"
//          "  shutdown                     shutdown camera (soft)\n"
//          "  reboot                       reboot camera\n"
//          "  reboot <filename>            reboot camera using specified firmware update\n"
//          "  reboot-fi2                   reboot camera using default firmware update\n"
//          "m memory <address>             get byte at address\n" 
//          "m memory <address>-<address>   get bytes at given range\n" 
//          "m memory <address> <num>       get num bytes at given address\n"
//          "  set <address> <long>         set long value at address\n"
//          "c call <address> <arg1> ...    call function at address with given arguments\n"
//          "  prop <id>                    get value of property\n"
//          "  prop <id>-<id>               get values in property range\n"
//          "  prop <id> <num>              get num values of properties starting at id\n"
//          "  param <id>                   get value of parameter\n"
//          "  param <id>-<id>              get values in parameter range\n"
//          "  param <id> <num>             get num values of parameters starting at id\n"
//          "u upload <local> <remote>      upload local file to camera\n"
//          "d download <remote> <local>    download file from camera\n"
//          "  lua <code>                   execute lua code\n"
//          "  luar <code>                  execute \"return <code>\" and retreive result\n"
//          "  script-support               which script interfaces does this build support\n"
//          "                               (bit 0 set = lua)\n"
//          "  script-status                script execution status (bit 0 set = lua)\n"
          );
      
    } else if ( !strcmp("r",buf) || !strcmp("reset",buf) )
    {
      reset_connection();

    } else if ( !strcmp("version",buf) )
    {
      int major, minor;
      printf("ptpcam: %i.%i\n",PTP_CHDK_VERSION_MAJOR,PTP_CHDK_VERSION_MINOR);
      if ( ptp_chdk_get_version(&params,&params.deviceinfo,&major,&minor) )
      {
        printf("camera: %i.%i\n",major,minor);
      }

    } else if ( !strcmp("shutdown-hard",buf) )
    {
      if ( ptp_chdk_shutdown_hard(&params,&params.deviceinfo) )
      {
        connected = 0;
      }

    } else if ( !strcmp("shutdown",buf) )
    {
      if ( ptp_chdk_shutdown_soft(&params,&params.deviceinfo) )
      {
        connected = 0;
      }

    } else if ( !strcmp("reboot",buf) )
    {
      if ( ptp_chdk_reboot(&params,&params.deviceinfo) )
      {
        connected = 0;
        sleep(2);
		open_connection();
	  }
		sleep(4);
		} else if ( !strcmp("reboot-fi2",buf) )
			{
      if ( ptp_chdk_reboot_fw_update("A/PS.FI2",&params,&params.deviceinfo) )
      {
        connected = 0;
        sleep(2);
        open_connection();
      }

    } else if ( !strncmp("reboot ",buf,7) )
    {
      char *s;
      if ( (s = strchr(buf,'\r')) != NULL )
      {
        *s = '\0';
      }
      if ( (s = strchr(buf,'\n')) != NULL )
      {
        *s = '\0';
      }
      if ( ptp_chdk_reboot_fw_update(buf+7,&params,&params.deviceinfo) )
      {
        connected = 0;
        sleep(2);
        open_connection();
      }
      
    } else if ( !strncmp("m ",buf,2) || !strncmp("memory ",buf,7) )
    {
      int start;
      int end;
      char *s;
      char *buf2;
      
      buf2 = strchr(buf,' ')+1;

      if ( (s = strchr(buf2,'-')) != NULL )
      {
        *s = '\0';
        start = strtoul(buf2,NULL,0);
        end = strtoul(s+1,NULL,0)+1;
      } else if ( (s = strchr(buf2,' ')) != NULL )
      {
        *s = '\0';
        start = strtoul(buf2,NULL,0);
        end = start+strtoul(s+1,NULL,0);
      } else {
        start = strtoul(buf2,NULL,0);
        end = start+1;
      }
     
      if ( (buf2 = ptp_chdk_get_memory(start,end-start,&params,&params.deviceinfo)) == NULL )
      {
        printf("error getting memory\n");
      } else {
        hexdump(buf2,end-start,start);
        free(buf2);
      }
      
    } else if ( !strncmp("set ",buf,4) )
    {
      int addr;
      int val;
      char *s;

      if ( (s = strchr(buf+4,' ')) == NULL )
      {
        printf("invalid arguments\n");
      } else {
        *s = '\0';
        addr = strtoul(buf+4,NULL,0);
        val = strtoul(s+1,NULL,0);
      
        if ( !ptp_chdk_set_memory_long(addr,val,&params,&params.deviceinfo) )
        {
          printf("set failed!\n");
        }
      }

    } /*else if ( !strncmp("c ",buf,2) || !strncmp("call ",buf,5) )
    {
      int num_args,i,ret;
      char *buf2;
      int *args;
      
      buf2 = buf;
      num_args = 0;
      while ( (buf2 = strchr(buf2,' ')) != NULL )
      {
        num_args++;
        buf2++;
      }
      args = malloc(num_args*sizeof(int));
      buf2 = buf;
      i = 0;
      while ( (buf2 = strchr(buf2,' ')) != NULL )
      {
        buf2++;
        args[i] = strtoul(buf2,NULL,0);
        i++;
      }

      if ( !ptp_chdk_call(args,num_args,&ret,&params,&params.deviceinfo) )
      {
        printf("error making call\n");
      } else {
        printf("%08x %i\n",ret,ret);
      }
      free(args);
      
    }*/ else if ( !strncmp("prop ",buf,5) )
    {
      int start;
      int end;
      char *s;
      int *vals;

      if ( (s = strchr(buf+5,'-')) != NULL )
      {
        *s = '\0';
        start = strtoul(buf+5,NULL,0);
        end = strtoul(s+1,NULL,0)+1;
      } else if ( (s = strchr(buf+5,' ')) != NULL )
      {
        *s = '\0';
        start = strtoul(buf+5,NULL,0);
        end = start+strtoul(s+1,NULL,0);
      } else {
        start = strtoul(buf+5,NULL,0);
        end = start+1;
      }
      
      if ( (vals = ptp_chdk_get_propcase(start,end-start,&params,&params.deviceinfo)) == NULL )
      {
        printf("error getting properties\n");
      } else {
        int i;
        for (i=start; i<end; i++)
        {
          printf("%3i: %i\n",i,vals[i-start]);
        }
        hexdump((char *) vals,(end-start)*4,start*4);
        free(vals);
      }
      
    } else if ( !strncmp("param ",buf,6) )
    {
      int start;
      int end;
      char *s;
      char *buf2;

      if ( (s = strchr(buf+6,'-')) != NULL )
      {
        *s = '\0';
        start = strtoul(buf+6,NULL,0);
        end = strtoul(s+1,NULL,0)+1;
      } else if ( (s = strchr(buf+6,' ')) != NULL )
      {
        *s = '\0';
        start = strtoul(buf+6,NULL,0);
        end = start+strtoul(s+1,NULL,0);
      } else {
        start = strtoul(buf+6,NULL,0);
        end = start+1;
      }
      
      if ( (buf2 = ptp_chdk_get_paramdata(start,end-start,&params,&params.deviceinfo)) == NULL )
      {
        printf("error getting parameter data\n");
      } else {
        int i;
        char *p = buf2;
        for (i=start; i<end; i++)
        {
          int t = *((int *) p);
          p += 4;
          printf("%03i: ",i);
          print_safe(p,t);
          printf(" (len=%i",t);
          if ( t == 1 )
          {
            printf(",val=%u)",*p);
          } else if ( t == 2 )
          {
            printf(",val=%u)",*((short *) p));
          } else if ( t == 4 )
          {
            printf(",val=%u)",*((int *) p));
          }
          printf(")\n");
          p += t;
        }
        hexdump(buf2,p-buf2,0);
        free(buf2);
      }

    } else if ( !strncmp("upload ",buf,7) )
    {
      char *s;

      if ( (s = strchr(buf,'\r')) != NULL )
      {
        *s = '\0';
      }
      if ( (s = strchr(buf,'\n')) != NULL )
      {
        *s = '\0';
      }

      if ( (s = strchr(buf+7,' ')) == NULL )
      {
        printf("invalid arguments\n");
      } else {
        *s = '\0';
        s++;
      
        if ( !ptp_chdk_upload(buf+7,s,&params,&params.deviceinfo) )
        {
          printf("upload failed!\n");
        }
      }

    } /* else if ( !strncmp("download ",buf,9) )
    {
      char *s;

      if ( (s = strchr(buf,'\r')) != NULL )
      {
        *s = '\0';
      }
      if ( (s = strchr(buf,'\n')) != NULL )
      {
        *s = '\0';
      }

      if ( (s = strchr(buf+9,' ')) == NULL )
      {
        printf("invalid arguments\n");
      } else {
        *s = '\0';
        s++;
      
        if ( !ptp_chdk_download(buf+9,s,&params,&params.deviceinfo) )
        {
          printf("download failed!\n");
        }
      }

    } */ else if ( !strcmp("list",buf) )
    {/* added by Jin */
	    list_files(camera_bus, camera_dev, camera_force);
    } else if (!strncmp("change_mode ", buf, strlen("change_mode ")))
    {
		change_mode(camera_bus, camera_dev, camera_force, buf+strlen("change_mode "));	
		
    } else if (!strcmp("c", buf) || !strcmp("capture", buf))
	{
		if ( !ptp_chdk_exec_lua("click(\"shoot_full\")" ,0 ,&params ,&params.deviceinfo) )
      	{
        	printf("execution failed!\n");
      	}
		else 
		{
			write(gpsfd, GPSMESSAGE, strlen(GPSMESSAGE));
	  	}	
		sleep(4);
	} else if (!strcmp("download", buf))
    {/* added by Haoyuan */
            get_all_files(camera_bus, camera_dev, camera_force, SKIP_IF_EXISTS);
    } else if (!strncmp("timelapse ", buf, 10) || !strncmp("t ", buf, 2))
    {/* added by Haoyuan*/
      // Command simplified by Vinh

      // currently works for periods greater than 3s
      // crashes at 2s if more than 35 photos are taken in a row

      int buffOffset;
      int minPeriod = 2;

      if (!strncmp("timelapse ", buf, 10))
      {
          buffOffset = 10;
      } else
      {
          buffOffset = 2;
      }

      // Get period
      int lapse = strtod(buf+buffOffset,NULL);

      // Check if period is valid
      if ( lapse < minPeriod )
      {
        printf("System currently can only handle integer periods of %ds or more\n", minPeriod);
		continue;
      } else
      {
        // Run timelapse
       timelapse(camera_bus, camera_dev, camera_force, lapse);	
      }
    }else if ( !strncmp("mode ",buf,5) )
    {
	  unsigned long mode = -1;
	  mode = strtoul(buf+5, NULL, 0);
	  if (mode != 0 && mode != 1) {
		fprintf(stderr, "invalid mode!\n");
	  }
      else if ( !ptp_chdk_switch_mode(mode,&params,&params.deviceinfo) )
      {
        printf("mode switch failed!\n");
      }

    } else if (!strncmp("shutter ", buf, 8) || !strncmp("s ",buf,2))
    {/* added by Vinh */
      // Shutterspeed must between 1/4 s and 1/2000 s
      int buffOffset;

      if (!strncmp("shutter ", buf, 8))
      {
          buffOffset = 8;
      } else
      {
          buffOffset = 2;
      }

      unsigned int inValue = strtoul(buf+buffOffset,NULL,0);
	  unsigned int value_tv96 = 0;
          unsigned int value;
	  char set_shutterspeed_command[256];
	  memset(set_shutterspeed_command, 0, sizeof(set_shutterspeed_command));

          // Round user input to camera's limited range of available
          // speeds
          if (inValue < 2000 && inValue >= 1800)
          {
               value = 2000;
          } else if (inValue < 1800 && inValue >= 1425)
          {
               value = 1600;
          } else if (inValue < 1425 && inValue >= 1125)
          {
               value = 1250;
          } else if (inValue < 1125 && inValue >= 900)
          {
               value = 1000;
          } else if (inValue < 900 && inValue >= 720)
          {
               value = 800;
          } else if (inValue < 720 && inValue >= 570)
          {
               value = 640;
          } else if (inValue < 570 && inValue >= 450)
          {
               value = 500;
          } else if (inValue < 450 && inValue >= 360)
          {
               value = 400;
          } else if (inValue < 360 && inValue >= 285)
          {
               value = 320;
          } else if (inValue < 285 && inValue >= 225)
          {
               value = 250;
          } else if (inValue < 225 && inValue >= 180)
          {
               value = 200;
          } else if (inValue < 180 && inValue >= 143)
          {
               value = 160;
          } else if (inValue < 143 && inValue >= 113)
          {
               value = 125;
          } else if (inValue < 113 && inValue >= 90)
          {
               value = 100;
          } else if (inValue < 90 && inValue >= 70)
          {
               value = 80;
          } else if (inValue < 70 && inValue >= 55)
          {
               value = 60;
          } else if (inValue < 55 && inValue >= 45)
          {
               value = 50;
          } else if (inValue < 45 && inValue >= 35)
          {
               value = 40;
          } else if (inValue < 35 && inValue >= 28)
          {
               value = 30;
          } else if (inValue < 28 && inValue >= 23)
          {
               value = 25;
          } else if (inValue < 23 && inValue >= 18)
          {
               value = 20;
          } else if (inValue < 18 && inValue >= 14)
          {
               value = 15;
          } else if (inValue < 14 && inValue >= 12)
          {
               value = 13;
          } else if (inValue < 12 && inValue >= 9)
          {
               value = 10;
          } else if (inValue < 9 && inValue >= 7)
          {
               value = 8;
          } else
          { 
               value = inValue;
          }

          // Notify user if rounding was done
          if (value != inValue)
          {
               printf("Shutter speed was rounded to 1/%d s\n", value);
          }

          // Numbers determined after manually checking camera parameters with
          // command 'lua set_user_tv96(xx)'
	  switch(value){
			case 4: value_tv96 = 192;
					break;
			case 5: value_tv96 = 224;
					break;
			case 6: value_tv96 = 256;
					break;
			case 8: value_tv96 = 288;
					break;
			case 10: value_tv96 = 320;
					break;
			case 13: value_tv96 = 352;
					break;
			case 15: value_tv96 = 384;
					break;
			case 20: value_tv96 = 416;
					break;
			case 25: value_tv96 = 448;
					break;
			case 30: value_tv96 = 480;
					break;
			case 40: value_tv96 = 512;
					break;
			case 50: value_tv96 = 544;
					break;
			case 60: value_tv96 = 576;
					break;
			case 80: value_tv96 = 608;
					break;
			case 100: value_tv96 = 640;
					break;
			case 125: value_tv96 = 672;
					break;
			case 160: value_tv96 = 704;
					break;
			case 200: value_tv96 = 736;
					break;
			case 250: value_tv96 = 768;
					break;
			case 320: value_tv96 = 800;
					break;
			case 400: value_tv96 = 832;
					break;
			case 500: value_tv96 = 864;
					break;
			case 640: value_tv96 = 896;
					break;
			case 800: value_tv96 = 928;
					break;
			case 1000: value_tv96 = 960;
					break;
			case 1250: value_tv96 = 992;
					break;
			case 1600: value_tv96 = 1021;
					break;
			case 2000: value_tv96 = 1053;
					break;
			default:
					break;
	  }
      if ( value_tv96 == 0 )
      {
        printf("Invalid shutter speed value! Must be between 1/4-1/2000 s\n");
		continue;
      }
	  if (value_tv96 != 0 ){
			  sprintf(set_shutterspeed_command, "set_user_tv96(\"%u\")", value_tv96);
			  //printf("set shutter speed command is %s\n", set_shutterspeed_command);
		   	  if ( !ptp_chdk_exec_lua(set_shutterspeed_command ,0 ,&params ,&params.deviceinfo) )
      		  {
        		printf("execution failed!\n");
      	      }
	  }
    } else if (!strncmp("iso ", buf, 4) || !strncmp("i ", buf, 2))
    {/* added by Vinh */
      // iso must be 100, 200, 400, or 800
      int buffOffset;

      if (!strncmp("iso ", buf, 4))
      {
          buffOffset = 4;
      } else
      {
          buffOffset = 2;
      }

      unsigned int value = strtoul(buf+buffOffset,NULL,0);
	  unsigned int value_iso96 = 0;
	  char set_iso_command[256];
	  memset(set_iso_command, 0, sizeof(set_iso_command));

	  switch(value){
			case 80: value_iso96 = 1;
					break;
			case 100: value_iso96 = 2;
					break;
			case 200: value_iso96 = 3;
					break;
			case 400: value_iso96 = 4;
					break;
			case 800: value_iso96 = 5;
					break;
			default:
					break;
	  }
      if ( value_iso96 == 0 )
      {
        printf("Invalid ISO value! Please look at the help.\n");
		continue;
      }
	  if (value_iso96 != 0 ){
			  sprintf(set_iso_command, "set_iso_mode(\"%u\")", value_iso96);
			  //printf("set iso command is %s\n", set_iso_command);
		   	  if ( !ptp_chdk_exec_lua(set_iso_command ,0 ,&params ,&params.deviceinfo) )
      		  {
        		printf("execution failed!\n");
      	      }
	  }

    } /*else if (!strncmp("autof ", buf, 6))
    {// added by Vinh 
      // autofocus parameter can only be 0 or 1
      int value = strtoul(buf+6,NULL,0);
      char set_autof_command[256];
      memset(set_autof_command, 0, sizeof(set_autof_command));

      if ( value !=0 && value != 1 )
      {
        printf("Value must be 0 or 1!\n");
	continue;
      } else
      {
        // Value accepted by lua command is inverted from user input
	sprintf(set_autof_command, "set_aflock(\"%u\")", !value);
	//printf("set autofocus command is %s\n", set_autof_command);
	if ( !ptp_chdk_exec_lua(set_autof_command ,0 ,&params ,&params.deviceinfo) )
      	{
           printf("execution failed!\n");
      	}
      }
    } */else if (!strncmp("aperture ", buf, 9) || !strncmp("a ", buf, 2))
    {/* added by Vinh */
      // aperture must be between 2.8-8.0
      // Shutterspeed must between 1/4 s and 1/2000 s
      int buffOffset;

      if (!strncmp("aperture ", buf, 9))
      {
          buffOffset = 9;
      } else
      {
          buffOffset = 2;
      }

      double inValue = strtod(buf+buffOffset,NULL);
	  unsigned int value_av96 = 0;
          double value;
	  char set_aperture_command[256];
	  memset(set_aperture_command, 0, sizeof(set_aperture_command));

          // Round user input to camera's limited range of available
          // apertures
          if (inValue < 8.0 && inValue >= 7.5)
          {
               value = 8.0;
          } else if (inValue < 7.5 && inValue >= 6.7)
          {
               value = 7.1;
          } else if (inValue < 6.7 && inValue >= 5.95)
          {
               value = 6.3;
          } else if (inValue < 5.95 && inValue >= 5.3)
          {
               value = 5.6;
          } else if (inValue < 5.3 && inValue >= 4.75)
          {
               value = 5.0;
          } else if (inValue < 4.75 && inValue >= 4.25)
          {
               value = 4.5;
          } else if (inValue < 4.25 && inValue >= 3.75)
          {
               value = 4.0;
          } else if (inValue < 3.75 && inValue >= 3.35)
          {
               value = 3.5;
          } else if (inValue < 3.35 && inValue >= 3.0)
          {
               value = 3.2;
          } else if (inValue < 3.0 && inValue >= 2.8)
          {
               value = 2.8;
          } else
          { 
               value = inValue;
          }

          // Notify user if rounding was done
          if (value != inValue)
          {
               printf("Aperture was rounded to %f\n", value);
          }

          // Cast value to int for case switching
	  //printf("value is %lf\n", value); 
          double temp = ((double)value*(double)10.0);
 	  //printf("temp is: %lf\n", temp); fflush(stdout);
	  int cValue = (int)temp;
          //printf("cValue is: %d\n", cValue); fflush(stdout);

          // Numbers determined after manually checking camera parameters with
          // command 'lua set_user_av96(xx)'
	  switch(cValue){
			case 28: value_av96 = 283;
					break;
			case 32: value_av96 = 320;
					break;
			case 35: value_av96 = 352;
					break;
			case 40: value_av96 = 384;
					break;
			case 45: value_av96 = 416;
					break;
			case 50: value_av96 = 448;
					break;
			case 56: value_av96 = 480;
					break;
			case 63: value_av96 = 512;
					break;
			case 71: value_av96 = 544;
					break;
			case 80: value_av96 = 576;
					break;
			default:
					break;
	  }
      if ( value_av96 == 0 )
      {
        printf("Invalid aperture value! Must be between F2.8-F8.0\n");
		continue;
      }
	  if (value_av96 != 0 ){
			  sprintf(set_aperture_command, "set_user_av96(\"%u\")", value_av96);
			  //printf("set aperture command is %s\n", set_aperture_command);
		   	  if ( !ptp_chdk_exec_lua(set_aperture_command ,0 ,&params ,&params.deviceinfo) )
      		  {
        		printf("execution failed!\n");
      	      }
	  }
    } else if (!strncmp("zoom ", buf, 5) || !strncmp("z ", buf, 2))
    {/* added by Vinh */
      // zoom factor must be between -15 and 15            
      int buffOffset;

      if (!strncmp("zoom ", buf, 5))
      {
          buffOffset = 5;
      } else
      {
          buffOffset = 2;
      }

      int value = strtol(buf+buffOffset,NULL,0);
	  if (value == 0) {
		fprintf(stderr, "Invalid zoom\n");
		continue;
	  }
      char set_zoom_command[256];
      memset(set_zoom_command, 0, sizeof(set_zoom_command));

      if ( value < -15 || value > 15 )
      {
        printf("Zoom factor must be between -15 and 15!\n");
	continue;
      } else
      {
        sprintf(set_zoom_command, "set_zoom_rel(\"%d\")", value);
	//printf("set zoom command is %s\n", set_zoom_command);
        if ( !ptp_chdk_exec_lua(set_zoom_command ,0 ,&params ,&params.deviceinfo) )
        {
             printf("execution failed!\n");
      	}
      }
    } else if ( !strncmp("lua ",buf,4) )
    {
      if ( !ptp_chdk_exec_lua(buf+4,0,&params,&params.deviceinfo) )
      {
        printf("execution failed!\n");
      }

    } else if ( !strncmp("luar ",buf,5) )
    {
      if ( !ptp_chdk_exec_lua(buf+5,1,&params,&params.deviceinfo) )
      {
        printf("execution failed!\n");
      }
    } else if ( !strcmp("script-support",buf) )
    {
	  int status;
      if ( ptp_chdk_get_script_support(&params,&params.deviceinfo,&status) )
      {
        printf("script-support:%x\n",status);
      }
    } else if ( !strcmp("script-status",buf) )
    {
	  int status;
      if ( ptp_chdk_get_script_status(&params,&params.deviceinfo,&status) )
      {
        printf("script-running:%x\n",status);
      }
#if 0
    } else if ( !strcmp("tasks",buf) )
    {
      int p,p2,p3,p4;
      int num_tasks, i, r;
      int call_args[11];
      char *buf;
      
      call_args[0] = 0xFF8139A8; // malloc
      call_args[1] = 0x100;
      ptp_chdk_call(call_args,2,&p,&params,&params.deviceinfo);

      call_args[0] = 0xFF82288C; // get task ids
      call_args[1] = p;
      call_args[2] = p+4;
      ptp_chdk_call(call_args,3,&num_tasks,&params,&params.deviceinfo);
      // get pointer to task-id array
      buf = ptp_chdk_get_memory(p,4,&params,&params.deviceinfo);
      memcpy(&p2,buf,4);
      free(buf);

      for (i=0; i<num_tasks; i++)
      {
        // get task id
        int id;
        buf = ptp_chdk_get_memory(p2+4*i,4,&params,&params.deviceinfo);
        memcpy(&id,buf,4);
        free(buf);
        printf("%x: ",id);

        call_args[0] = 0xFF813380; // get task info
        call_args[1] = id;
        call_args[2] = p+8; // dest
        ptp_chdk_call(call_args,3,&r,&params,&params.deviceinfo);
        if ( r )
        {
          break;
        }

        // get pointer to name string
        buf = ptp_chdk_get_memory(p+8+0x2c,4,&params,&params.deviceinfo);
        memcpy(&p3,buf,4);
        free(buf);
        call_args[0] = 0xFF8156BC; // strlen
        call_args[1] = p3;
        ptp_chdk_call(call_args,2,&r,&params,&params.deviceinfo);
        // get name
        buf = ptp_chdk_get_memory(p3,r+1,&params,&params.deviceinfo);
        printf("%s (",buf);
        free(buf);

        // get state id
        buf = ptp_chdk_get_memory(p+8+0,4,&params,&params.deviceinfo);
        memcpy(&p3,buf,4);
        free(buf);
        call_args[0] = 0xFF822848; // get state
        call_args[1] = id;
        call_args[2] = p3;
        ptp_chdk_call(call_args,3,&p4,&params,&params.deviceinfo);
        call_args[0] = 0xFF8156BC; // strlen
        call_args[1] = p4;
        ptp_chdk_call(call_args,2,&r,&params,&params.deviceinfo);
        // get name
        buf = ptp_chdk_get_memory(p4,r+1,&params,&params.deviceinfo);
        printf("%i:%s, prio ",p3,buf);
        free(buf);
        
        // get priority
        buf = ptp_chdk_get_memory(p+8+4,4,&params,&params.deviceinfo);
        memcpy(&p3,buf,4);
        free(buf);
        printf("%i)\n",p3);

        // XXX add stack info
      }

    } else if ( !strcmp("log",buf) )
    {
      struct log_struct {
        int log_on;
        int log_level;
        int p1;
        int p2;
        int bufsize; //??
      } *log;
      int *data;
      int *listp, liststart;
      struct llist {
        int next;
        int size;
        int len;
      } *list;
      char *s;
      
      // 0x5598 found in eventproc_export_*CameraLog* functions
      if ( (log = (struct log_struct *) ptp_chdk_get_memory(0x5598,20,&params,&params.deviceinfo)) == NULL )
      {
        printf("error\n");
      } else {
        printf("%i %i 0x%x 0x%x %i\n",log->log_on,log->log_level,log->p1,log->p2,log->bufsize);
        if ( (data = (int *) ptp_chdk_get_memory(log->p1+0x40,4,&params,&params.deviceinfo)) == NULL )
        {
          printf("error\n");
        } else {
          printf("0x%x\n",*data);
          if ( (listp = (int *) ptp_chdk_get_memory((*data)+4,4,&params,&params.deviceinfo)) == NULL )
          {
            printf("error\n");
          } else {
            printf("0x%x\n",*listp);
            liststart = *listp;
            while ( 1 )
            {
              if ( (list = (struct llist *) ptp_chdk_get_memory(*listp,12,&params,&params.deviceinfo)) == NULL )
              {
                printf("error\n");
                break;
              } else {
                printf("0x%x %i %i\n",list->next,list->size,list->len);
                s = NULL;
                if ( list->len > 0 && (s = ptp_chdk_get_memory((*listp)+12,list->len,&params,&params.deviceinfo)) == NULL )
                {
                  printf("error\n");
                  free(list);
                  break;
                } else {
                  printf("%s",s);
                  free(s);
                }
                *listp = list->next;
                free(list);
              }
              if ( *listp == liststart )
              {
                break;
              }
            }
            free(listp);
          }
          free(data);
        }
        free(log);
      }

    } else if ( !strcmp("upgrade",buf) )
    {
      if ( !ptp_chdk_upload("/home/muck/chdk/bin/PS.FI2","A/PS.FI2",&params,&params.deviceinfo) )
      {
          printf("upload failed\n");
      } else {
        if ( ptp_chdk_reboot_fw_update("A/PS.FI2",&params,&params.deviceinfo) )
        {
          connected = 0;
          sleep(3);
          open_connection();
        } else {
          printf("reboot failed\n");
        }
      }
#endif
    } else {
      printf("unknown command\n");
    }
    // in command line mode parse command once and then exit:
    if (chdkmode==CHDK_MODE_CLI)
      break;
  }

  if ( connected )
  {
    close_connection();
  }

  return 0;
}

